OpenFeign 自定义拦截器
假设现在有这么一个工程 mall4cloud,它有两个模块,order-openfeign、user。
order-openfeign 通过 feign 调用 user。
一、拦截器原理
OpenFeign 作为微服务间接口的调用组件,除了需要考虑传递消息体外,还需要考虑到如何在各个服务间传递请求头信息。如果不做任何配置,直接使用openFeign在服务间进行调用就会如下图:
这样会丢失请求头,在企业级的应用中,token 是非常重要的请求信息,他会携带权限、用户信息等。
OpenFeign 在远程调用之前会遍历容器中的 RequestInterceptor,调用 RequestInterceptor 的apply 方法,创建一个新的 Request 进行远程服务调用。因此可以通过实现 RequestInterceptor 给容器中添加自定义的 RequestInterceptor 实现类,在这个类里面设置需要发送请求时的参数,比如请求头信息,链路追踪信息等。
package feign;public interface RequestInterceptor {void apply(RequestTemplate var1);
}
二、使用场景
通过拦截器实现参数传递。
常用场景:统一添加 header 信息。(比如服务提供者传递全局事务XID,会员ID,认证 token 令牌,链路追踪 traceID 等等)
三、模拟实现
3.1、自定义拦截器实现 RequestInterceptor 接口
@Slf4j
public class TokenAuthRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();if (requestAttributes != null) {HttpServletRequest request = requestAttributes.getRequest();String access_token = request.getHeader("Authorization");log.info("从Request中解析出token:{}", access_token);//设置tokentemplate.header("Authorization",access_token);}}
}
3.2、JavaBean 配置拦截器
3.2.1、新建配置类
public class FeignInterceptorConfig {@Beanpublic TokenAuthRequestInterceptor tokenAuthRequestInterceptor() {return new TokenAuthRequestInterceptor();}
}
3.2.2、在 UserFeignService @FeignClient 注解 configuration 指定这个配置类
@FeignClient(value = "user-service", path = "/user", configuration = FeignInterceptorConfig.class)
public interface UserFeignService {@RequestMapping("/findUserNeedAuth/{id}")String findUserNeedAuth(@PathVariable("id") String id);
}
3.3、OrderController 调用 UserFeignService
@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate UserFeignService userFeignService;@RequestMapping("/placeOrderNeedAuth/{userId}")public String placeOrderNeedAuth(@PathVariable String userId) {String username = userFeignService.findUserNeedAuth(userId);return "用户[" + username + "]下单成功!";}
}
3.4、UserController 校验 token
@RestController
@RequestMapping("/user")
public class UserController {@Value("${server.port}")private String port;@RequestMapping("/findUserNeedAuth/{id}")public String findUserNeedAuth(@PathVariable String id, @RequestHeader("Authorization") String token) {if (StringUtils.isBlank(token)) {throw new IllegalArgumentException("参数缺失!");}// 校验tokenif (!"feign123".equals(token)) {throw new IllegalArgumentException("token错误!");}// 模拟从数据库获取用户Map<String, String> map = new HashMap<>();map.put("1", "小林:"+port);map.put("2", "小王:"+port);return map.get(id);}
}
这段代码只是进行模拟校验。
- 实际不可能在每个方法中进行 token 校验。
- 校验失败抛出的异常也需要进行统一异常处理。
3.5、调用测试
GET http://localhost:8011/order/placeOrderNeedAuth/1 Accept: application/json Authorization: feign123
Order 模块日志:
3.6、yml 配置拦截器
除了在 JavaBean 中配置拦截器外,还可以在 yml 中配置拦截器。
3.6.1、去掉 UserFeignService @FeignClient 注解 configuration 配置
3.6.2、在 order 模块 application.yml 配置
测试结果是一样的。