实现一个自己的OpenFeign 远程调用验证协议--Springboot 自定义拦截器验证请求合法性--xunznux

Springboot 如何实现一个自定义的拦截器处理系统发出的请求以及接收到的请求(实现一个用于feign远程调用验证的简单协议)

文章目录

  • Springboot 如何实现一个自定义的拦截器处理系统发出的请求以及接收到的请求(实现一个用于feign远程调用验证的简单协议)
    • **实现Feign拦截器的意义**:
    • 实现细节&代码
      • 首先添加 application.yaml 的配置
      • 添加配置类
        • XunConfig
        • XunProperties
        • XunAutoConfiguration
      • ClientInterceptor 客户端拦截器的实现
      • ServerInterceptor 服务端拦截器
      • 服务端提供的接口方法
      • 客户端的Feign调用方法
      • Springboot 启动类增加客户端
      • 总结

实现Feign拦截器的意义

通过 Spring Boot中的过滤器,可以处理HTTP请求并执行一些预处理逻辑,比如验证请求的合法性等。

  • 统一处理请求:
    拦截器允许你在所有Feign调用之前和之后执行代码,这使得你可以统一处理如添加请求头、日志记录、性能监控、错误处理等操作。
  • 权限校验:
    在微服务架构中,服务间通信的安全性非常重要。通过Feign拦截器,可以在调用远程服务前检查权限,确保只有授权的服务才能访问特定资源。
  • 认证和授权:
    当使用OAuth2或其他认证机制时,Feign拦截器可以自动添加必要的认证信息(如JWT Token)到请求头中,确保远程服务能够识别调用者的身份。
  • 请求/响应修改:
    拦截器可以修改请求参数或响应数据,例如,对敏感信息进行加密或解密,或者根据业务需求转换数据格式。
  • 性能优化:
    可以在拦截器中实现缓存逻辑,避免不必要的远程调用,提高应用性能。

实现细节&代码

具体可以看代码仓库的demo:https://gitee.com/zhou-xiujun/feign-xun-interceptor

首先添加 application.yaml 的配置

spring:application:name: FeignXunInterceptor
app:id: 999key: 666xun:protocol:context-path: /xun

其中,app.id 和 app.key 是需要保密的,用于服务端验证客户端的请求是否合法。
context-path 是用于判断请求路径是否符合该远程调用的规定,如果URI 以该路径开头,则判定是远程调用,需要进行请求的处理。

添加配置类

XunConfig
@Component
@ConfigurationProperties(prefix = "app")
public class XunConfig {private String id;private String key;// 添加getters和setterspublic String getId() {return id;}public void setId(String id) {this.id = id;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}
}

该配置类用于获取 id 和 key。

XunProperties
@Component
@ConfigurationProperties(prefix = "xun.protocol")
public class XunProperties {private String contextPath;public String getContextPath() {return contextPath;}public void setContextPath(String contextPath) {this.contextPath = contextPath;}
}

该配置类用于获取 contextPath。

XunAutoConfiguration
@Configuration
public class XunAutoConfiguration {@Beanpublic RequestInterceptor requestInterceptor() {return new ClientInterceptor();}@Beanpublic OncePerRequestFilter serverInterceptor() {return new ServerInterceptor();}
}

其中的 ClientInterceptor 和 ServerInterceptor 都是后面定义的客户端拦截器和服务端拦截器。这里使用 bean 注入的方式应用拦截器。

ClientInterceptor 客户端拦截器的实现

public class ClientInterceptor implements RequestInterceptor {@Resourceprivate XunConfig xunConfig;@Resourceprivate XunProperties xunProperties;private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";@Overridepublic void apply(RequestTemplate template) {if (template.url().startsWith(xunProperties.getContextPath())) {String appId = xunConfig.getId(); // 从配置或服务中获取String appKey = xunConfig.getKey(); // 从配置或服务中获取byte[] body = template.body(); // 需要从template或其他地方获取请求体内容Boolean signUpperCase = true; // 根据需要设置String sign = xunSign(appId, appKey, Optional.ofNullable(body).map(Arrays::toString).orElse(""), signUpperCase);template.header("Content-Type", "application/json");template.header("Authorization", "Bearer " + sign);if (body != null) {template.header("data", Base64.getEncoder().encodeToString(body));}process(template, StandardCharsets.UTF_8, sign, body);}}private void process(RequestTemplate template, Charset charset, String key, byte[] data) {template.removeHeader("Content-Type");template.header("Content-Type", "application/json");template.body(data, charset);}private String encode(String string, Charset charset) {try {return URLEncoder.encode(string, charset.name());} catch (UnsupportedEncodingException e) {throw new RuntimeException("Failed to encode URL", e);}}/*** 生成签名* @param appId 应用ID* @param appKey 应用密钥* @param bodyStr 请求体字符串* @param signUpperCase 是否大写签名* @return 生成的签名*/private String xunSign(String appId, String appKey, String bodyStr, Boolean signUpperCase) {try {String data = appId + (bodyStr != null ? bodyStr : "");Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);SecretKeySpec secretKey = new SecretKeySpec(appKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA256_ALGORITHM);mac.init(secretKey);byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));String signature = Base64.getEncoder().encodeToString(hash);return signUpperCase ? signature.toUpperCase() : signature;} catch (Exception e) {throw new RuntimeException("Failed to generate signature", e);}}}

apply方法,用于对RequestTemplate对象进行处理。具体功能如下:

  1. 首先判断template的URL是否以xunProperties.getContextPath()开头,如果是则执行以下步骤,否则直接返回。
  2. 从配置或服务中获取appId和appKey。
  3. 从template中获取请求体内容body。
  4. 根据需要设置signUpperCase为true。
  5. 调用xunSign方法生成签名sign。
  6. 设置请求头Content-Type为application/json,Authorization为Bearer + sign。
  7. 如果请求体body不为空,则将body进行Base64编码,并设置请求头data为编码后的字符串。
  8. 最后调用process方法对template进行进一步处理,传入UTF-8编码、sign和body作为参数。

该函数主要用于对请求进行签名认证和设置请求头,以便进行安全的API调用。这里的实现较为简单,可以个人对此进行优化改进,保证更好的安全性和合理性。主要展示是是这个处理流程。

ServerInterceptor 服务端拦截器

public class ServerInterceptor extends OncePerRequestFilter {@Resourceprivate XunConfig xunConfig;@Resourceprivate XunProperties xunProperties;private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 认证放行与协议验证鉴权逻辑String authorization = request.getHeader("Authorization");String requestURI = request.getRequestURI();if (requestURI.startsWith(xunProperties.getContextPath()) && authorization != null && authorization.startsWith("Bearer ")) {String appId = xunConfig.getId(); // 从配置或服务中获取String appKey = xunConfig.getKey(); // 从配置或服务中获取Boolean signUpperCase = true; // 根据需要设置String generatedSign = xunSign(appId, appKey, getRequestBody(request), signUpperCase);String token = authorization.substring(7);if (generatedSign.equals(token)) {filterChain.doFilter(request, response);} else {// 验证不通过throw new RuntimeException("验证不通过");}}// 放行filterChain.doFilter(request, response);}private String xunSign(String appId, String appKey, String bodyStr, Boolean signUpperCase) {try {String data = appId + (bodyStr != null ? bodyStr : "");Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);SecretKeySpec secretKey = new SecretKeySpec(appKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA256_ALGORITHM);mac.init(secretKey);byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));String signature = Base64.getEncoder().encodeToString(hash);return signUpperCase ? signature.toUpperCase() : signature;} catch (Exception e) {throw new RuntimeException("Failed to generate signature", e);}}private String getRequestBody(HttpServletRequest request) throws IOException {StringBuilder requestBody = new StringBuilder();try (BufferedReader reader = request.getReader()) {String line;while ((line = reader.readLine()) != null) {requestBody.append(line);}}return requestBody.toString();}
}

该函数是一个过滤器,用于对请求进行认证和授权验证。首先,它从请求头中获取Authorization信息和请求的URI。然后,判断请求的URI是否以某个特定的路径开头,并且Authorization信息是否以Bearer开头。如果是,则从配置或服务中获取appId和appKey,并根据需要生成一个签名。接着,从Authorization信息中获取token,并将其与生成的签名进行比较。如果两者相等,则放行请求,否则抛出一个运行时异常,表示验证不通过。最后,无论认证和授权验证是否通过,都会放行请求。
同样的,这里还可以再进行优化改进。

以上这两部分就是实现拦截器的核心部分。
接下来是使用部分。

服务端提供的接口方法

@RestController
@RequestMapping("/xun")
public class ServerController {@GetMapping("/hello")public String hello() {return "hello";}
}

客户端的Feign调用方法

@RestController
@RequestMapping("/client")
public class ClientController {@Resourceprivate SampleClient sampleClient;@GetMapping("/hello")public String hello() {// 调用远程服务return sampleClient.hello();}
}
@FeignClient(name = "server", url = "http://localhost:8080", configuration = XunAutoConfiguration.class)
public interface SampleClient {// 这定义了一个公共接口SampleClient,该接口将被Feign客户端实现,从而允许调用远程服务的方法。@GetMapping("/xun/hello")String hello();
}

@FeignClient 注解是Spring Cloud Feign提供的,用于声明一个Feign客户端。

  • name = “server” 指定了这个Feign客户端的名字,通常这个名字会被用来识别一个服务实例,特别是在使用服务注册与发现机制如Eureka或Consul时。但是在这个例子中,因为指定了URL,name可能不会用于服务发现。
  • url = “http://localhost:8080” 直接指定了目标服务的URL,这意味着这个Feign客户端将直接调用位于http://localhost:8080的微服务,而不是通过服务发现机制寻找服务位置。
  • configuration = XunAutoConfiguration.class 指定了一个配置类,该配置类包含了Feign客户端的额外配置,比如编码器、解码器、日志级别等。这使得可以为特定的Feign客户端定制行为。
  • @GetMapping 是Spring MVC的注解,用于映射HTTP GET请求到特定的方法上。在这里,它将/xun/hello的GET请求映射到了hello()方法。
  • String hello() 是一个无参数的方法,当远程服务的/xun/hello端点被调用时,它期望返回一个字符串类型的结果。

综上所述,SampleClient是一个Feign客户端接口,它可以调用位于http://localhost:8080/xun/hello的远程服务端点,并期待返回一个字符串类型的响应。在Spring Boot应用中,你可以像调用本地方法一样调用SampleClient的hello()方法,Feign框架会自动处理HTTP请求和响应的序列化与反序列化。

Springboot 启动类增加客户端

@SpringBootApplication
@EnableFeignClients(clients = {SampleClient.class})
public class FeignXunInterceptorApplication {public static void main(String[] args) {SpringApplication.run(FeignXunInterceptorApplication.class, args);}
}

总结

至此,一个简单的用于 OpenFeign 远程调用的验证协议就完成了,可以将其作为一个项目打包为jar包,然后引入其他项目中使用,这里将不做介绍。当然,拦截器的使用也不仅仅是可以只用于 OpenFeign 的远程调用验证,可以用于任何 HTTP 请求的拦截与验证。

之前的文章有对Springboot 启动时Bean的创建与注入这个过程的讲解以及对应的源码解读,感兴趣的可以去看看:
Springboot 启动时Bean的创建与注入(一)-源码解读-xunznux
Springboot 启动时Bean的创建与注入(二)-源码解读-xunznux
Springboot 的Bean生命周期五步、七步、十步详解以及框架源码解读

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/47817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

某企业网络及服务器规划与设计

目录 1. 项目需求与设计... 5 1.1 项目需求... 5 1.2 组建企业网络内部网的流程... 5 1) 构思阶段... 5 2) 方案设计阶段... 6 3) 工程实施阶段... 6 4) 测试验收... 6 5) 管理维护... 7 1.3 技术可行性分析... 7 1.4 网络组网规则... 8 1.5 网络拓扑... 8 2. 项目所…

【UE5】在Widget中显示3D网格体

效果 步骤 1. 新建一个工程并添加第三人称游戏资源包 2. 添加一个控件蓝图,这里命名为“WBP_3DOverlay”,用于显示3D网格体 打开“WBP_3DOverlay”,添加一个画布面板和一个背景模糊控件,这里模糊强度设置为10 3. 打开第三人称角色…

connect的非阻塞模式

本文参考:connect 函数在阻塞和非阻塞模式下的行为 一般情况下,在使用connect连接服务端时,需要等待一会儿才会函数才会返回,导致程序阻塞。为了降低阻塞的影响,我们可能会单独开个线程处理connect请求,例…

Spark-第一周

一、spark是什么 Spark是一种快速、通用、可扩展的大数据分析引擎 2009年诞生于加州大学伯克利分校AMPLab,2010年开源,2013年6月成为Apache孵化项目,2014年2月成为Apache顶级项目。 目前,Spark生态系统已经发展成为一个包含多个…

常见的数据集格式

常见的数据集格式有三种,分别为voc(xml)、coco(json)、yolo(txt)。 1 VOC VOC数据集由五个部分构成:JPEGImages,Annotations,ImageSets,SegmentationClass以及SegmentationObject. . └── VOC #根目…

HAL库源码移植与使用之RTC时钟

实时时钟(Real Time Clock,RTC),本质是一个计数器,计数频率常为秒,专门用来记录时间。 普通定时器无法掉电运行!但RTC可由VBAT备用电源供电,断电不断时 这里讲F1系列的RTC 可以产生三个中断信号&#xff…

Kafka之存储设计

文章目录 1. 分区和副本的存储结构1. 分区和副本的分布2. 存储目录结构3. 文件描述 2. 相关配置3. 数据文件类型4. 数据定位原理LogSegment 类UnifiedLog 类 5. 副本数据同步HW水位线LEO末端偏移量HW更新原理 6. 数据清除 1. 分区和副本的存储结构 在一个多 broker 的 Kafka 集…

文心一言大模型

文心一言是百度基于其强大的“文心”大模型技术推出的生成式AI产品(英文名:ERNIE Bot)。以下是关于文心一言的详细介绍: 一、产品定位与功能 定位:文心一言被定位为人工智能基座型的赋能平台,旨在助力金融…

8 Vue 开发方案

通用需求 axios 封装 封装 axios 请求 import axios from axios // 导入 axios// 第一种: 直接配置在 axios 上(无法配置多个) axios.defaults.baseURL http://ttapi.research.itcast.cn/// 第二种: 使用 create 方法创建一个 axios 实例化对象(能够配置多个) // 1. 设置基准…

MFC:以消息为基础的事件驱动系统和消息映射机制

以消息为基础的事件驱动系统和消息映射机制 (1)消息 A.What(什么是消息) 本质是一个数据结构,用于应用程序不同部分之间进行通信和交互 typedef struct tagMSG {HWND hwnd; // 接收该消息的窗口句柄UINT message; // 消息标…

【C语言】 利用栈完成十进制转二进制(分文件编译,堆区申请空间malloc)

利用栈先进后出的特性,在函数内部,进行除二取余的操作,把每次的余数存入栈内,最后输出刚好就是逆序输出,为二进制数 学习过程中,对存储栈进行堆区的内存申请时候,并不是很熟练,一开始…

双边性:构建神经网络的新方法

正如承诺的那样,这是最近我遇到的最有趣的想法之一的第二部分。如果你错过了,请务必观看本系列的第一部分 - 神经科学家对改进神经网络的看法 - 我们讨论了双边性的生物学基础以及我们大脑的不对称性质如何带来更高的性能。 在这篇文章中,我…

v-for 进行列表的 增删改查

通过对象下标替换属性值 但是通过实践此方法是错误的&#xff0c;Vue监听的是students这个对象&#xff0c;而不是这个对象里面的数组信息&#xff0c;也就是说&#xff0c;改变里面的值&#xff0c;并不能在页面上实现更新的功能 <!DOCTYPE html> <html lang"en…

springboot引入kafka

一. Kafka 简介 什么是 Kafka&#xff1f; Kafka 是一个分布式流处理平台&#xff0c;最初由 LinkedIn 开发&#xff0c;并于 2011 年开源。它用于构建实时数据管道和流应用&#xff0c;能够处理和分析流数据。Kafka 的核心概念 Producer&#xff08;生产者&#xff09;&#…

通俗地理解主动元数据管理

元数据管理&#xff0c;是企业开展数据管理的核心基础&#xff0c;内容涉及元数据的创建&#xff0c;确定需要捕获哪些元数据&#xff0c;通过哪些工具和流程进行创建&#xff0c;继而将元数据妥善存储&#xff0c;保障安全性和可访问性&#xff0c;并不断更新维护&#xff0c;…

git 总结2

记录今日学习内容&#xff1a; 项目组成&#xff1a; 一个master分支QA&#xff1a;开发分支bug修复分支个人开发功能需求分支 常用 从仓库拉取代码&#xff1a; git clone xxx创建并且切换到自己的分支&#xff1a;git checkout -b xxx切换到某分支&#xff1a;git checko…

开发面试算法题求教

在《无尽的拉格朗日》中&#xff0c;有许多不同的星系建筑物。每个星系建筑物的等级不同&#xff0c;带来的影响力也不同。 已知宇宙可以抽象为一个无穷大的平面直角坐标系&#xff0c;现在给定了每个星系建筑物的所在坐标(xi,yi)和它的影响力ri&#xff0c;距离其切比雪夫距离…

C++入门语法总结和STL回顾

目录 iostream和命名空间输入输出流循环输入输出 vector容器string字符串cpp链表操作hashTable哈希表set集合map映射范围for循环 stack栈queue队列list列表类和面向对象 iostream和命名空间 输入输出流 内置库iostream提供了输入和输出功能&#xff0c;允许开发者从键盘读取输…

[渗透测试] 反序列化漏洞

反序列化漏洞 ​ 序列化&#xff1a;将对象的状态信息转换为可以传输或存储的形式的过程。简单的来说&#xff0c;就是将一个抽象的对象转换成可以传输的字符串 &#xff0c;以特定的形式在进行之间实现跨平台的传输。 序列化大多以字节流、字符串、json串的形式来传输。将对…

linux/windows wps node.js插件对PPT状态监听并且通知其他应用

需求背景 公司要求对Window系统&#xff0c;和国产操作系统&#xff08;UOS&#xff09;的wps 软件在 PPT开始播放 结束播放&#xff0c;和播放中翻页 上一页 下一页 等状态进行监听&#xff0c;并通知到我们桌面应用。 技术方案 开发WPS插件&#xff0c;使用node.JS 插件开…