概述

有的时候业务场景是不走正常路线的,业务场景千奇百怪的需求都有, 有的时候权限校验需要在网关那里统一鉴权,有的时候就需要下放到某个微服务去处理(不在网关那里统一鉴权)

通常我们调用的接口都是有权限控制的,很多时候可能认证的值是通过参数去传递的,还有就是通过请求头去传递认证信息,比如 Basic 认证方式。 接口鉴权

Feign 中我们可以直接配置 Basic 认证

  1. @Configuration // 全局配置
  2. public class FeignConfig {
  3. @Bean
  4. public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
  5. return new BasicAuthRequestInterceptor("fox", "123456");
  6. }
  7. }

扩展点: feign.RequestInterceptor
每次 feign 发起http调用之前,会去执行拦截器中的逻辑。

  1. public interface RequestInterceptor {
  2. /**
  3. * Called for every request. Add data using methods on the supplied {@link RequestTemplate}.
  4. */
  5. void apply(RequestTemplate template);
  6. }

使用场景

  1. 统一添加 header 信息;
  2. 对 body 中的信息做修改或替换;

自定义拦截器实现认证逻辑

FeignConfig

  1. package feigndemo.config;
  2. import feigndemo.interceptor.FeignAuthRequestInterceptor;
  3. import feign.Logger;
  4. import feign.Request;
  5. import feign.codec.Decoder;
  6. import feign.codec.Encoder;
  7. import feign.jackson.JacksonDecoder;
  8. import feign.jackson.JacksonEncoder;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. @Configuration // 全局配置
  12. public class FeignConfig {
  13. /**
  14. * 日志级别
  15. * 通过源码可以看到日志等级有 4 种,分别是:
  16. * NONE:不输出日志。
  17. * BASIC:只输出请求方法的 URL 和响应的状态码以及接口执行的时间。
  18. * HEADERS:将 BASIC 信息和请求头信息输出。
  19. * FULL:输出完整的请求信息。
  20. */
  21. @Bean
  22. public Logger.Level feignLoggerLevel() {
  23. return Logger.Level.FULL;
  24. }
  25. /**
  26. * 自定义拦截器
  27. * @return
  28. */
  29. @Bean
  30. public FeignAuthRequestInterceptor feignAuthRequestInterceptor(){
  31. return new FeignAuthRequestInterceptor();
  32. }
  33. @Bean
  34. public Request.Options options() {
  35. return new Request.Options(5000, 5000);
  36. }
  37. @Bean
  38. public Decoder decoder() {
  39. return new JacksonDecoder();
  40. }
  41. @Bean
  42. public Encoder encoder() {
  43. return new JacksonEncoder();
  44. }
  45. }


FeignAuthRequestInterceptor

  1. package feigndemo.interceptor;
  2. import feign.RequestInterceptor;
  3. import feign.RequestTemplate;
  4. import java.util.UUID;
  5. public class FeignAuthRequestInterceptor implements RequestInterceptor {
  6. @Override
  7. public void apply(RequestTemplate template) {
  8. // 业务逻辑 模拟认证逻辑
  9. // 请求头携带token
  10. String access_token = "IAmToken"+UUID.randomUUID().toString();
  11. template.header("Authorization",access_token);
  12. }
  13. }

这样在feign远程调用的时候自动将Authorization携带到header里面了,然后服务的被调用方就可以从header里面获取这个信息了

feign输出:

  1. 2022-01-13 12:18:04.322 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] ---> GET http://mall-order/order/findOrderByUserId/1 HTTP/1.1
  2. 2022-01-13 12:18:04.323 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] Authorization: IAmToken53ce45ea-dc4f-4908-9492-e4f6ac43c1c2
  3. 2022-01-13 12:18:04.323 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] ---> END HTTP (0-byte body)
  4. 2022-01-13 12:18:04.640 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] <--- HTTP/1.1 200 (314ms)
  5. 2022-01-13 12:18:04.640 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] connection: keep-alive
  6. 2022-01-13 12:18:04.640 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] content-type: application/json
  7. 2022-01-13 12:18:04.641 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] date: Thu, 13 Jan 2022 04:18:04 GMT
  8. 2022-01-13 12:18:04.641 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] keep-alive: timeout=60
  9. 2022-01-13 12:18:04.641 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] transfer-encoding: chunked
  10. 2022-01-13 12:18:04.641 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId]
  11. 2022-01-13 12:18:04.641 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] {"msg":"success","code":0,"orders":[{"id":1,"userId":"1","commodityCode":"1","count":1,"amount":1}]}
  12. 2022-01-13 12:18:04.641 DEBUG 79432 --- [nio-8055-exec-1] feigndemo.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] <--- END HTTP (100-byte body)

服务提供者拦截器

当消费者调用这个服务提供者的时候,拦截器就可以在这里获取到header的token信息了.

  1. @Slf4j
  2. public class AuthInterceptor implements HandlerInterceptor {
  3. @Override
  4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
  5. boolean flag = true;
  6. // 简单的认证逻辑 从请求头中获取Authorization
  7. String authorization = request.getHeader("Authorization");
  8. log.info("=========Authorization:"+authorization);
  9. if (StringUtils.isEmpty(authorization)){
  10. // 从请求参数中获取access_token
  11. String access_token = request.getParameter("access_token");
  12. if(StringUtils.isEmpty(access_token)){
  13. flag = false;
  14. }
  15. }
  16. return flag;
  17. }
  18. }

代码地址

代码出自图灵学院, 我自己学完了做完作业又改造了一下.

https://gitee.com/zjj19941/ZJJ_Neaten5.10/tree/master/ZJJ_Feign/feign-interceptor

先执行sql脚本,自己准备一个nacos服务,然后修改配置文件配置,
最后,启动 com.order.MallOrderApplication 和 feigndemo.MallUserFeignDemoApplication
然后postman发起get请求: localhost:8055/user/findOrderByUserId/1
就能看到效果了

补充:可以在yml中配置


  1. feign:
  2. client:
  3. config:
  4. mall-order: #对应微服务
  5. requestInterceptors[0]: #配置拦截器
  6. feigndemo.interceptor.FeignAuthRequestInterceptor