好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Spring Boot Feign服务调用之间带token问题

Feign服务调服务传递数据带token验证

Feign服务调服务就不多做介绍了,值得提醒的是,Feign服务调服务传递数据的时候,比如某用户服务是需要带token验证的,而调用那个用户服务的时候报错,提示token为空,是因为Feign请求的时候没有带上token

解决方式

要解决这个问题,想必能猜到最方便的就是往请求头里加上token,一起带过去

Feign有提供一个接口,RequestInterceptor

只要实现这个接口,简单做一些处理,比如说我们验证请求头的token叫Access-Token,我们就先取出当前请求的token,然后放到feign请求头上

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import feign.RequestInterceptor;

import feign.RequestTemplate;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.context.request.RequestContextHolder;

import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**

  * Feign配置

  * 使用FeignClient进行服务间调用,传递headers信息

  */

@Configuration

public class FeignConfig implements RequestInterceptor {

     @Override

     public void apply(RequestTemplate requestTemplate) {

         ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();

         HttpServletRequest request = attributes.getRequest();

         //添加token

         requestTemplate.header( "Access-Token" , request.getHeader( "Access-Token" ));

     }

}

这样已经成功往Feign请求头里加上了Token,还可以这样,为了方便本地调试,可以在Spring Boot加上过滤器,每次本地调用没有Token的时候加上一个,只要实现Spring提供的Filter接口

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

import org.apache测试数据mons.lang3.StringUtils;

import javax.servlet.*;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletRequestWrapper;

import java.io.IOException;

/**

  * 每次请求过滤器拦截加Token

  */

public class AddTokenFilter implements Filter {

     /**

      * superAdmin

      */

     private static final String DEFAULT_TOKEN = "你的token" ;

     private String profilesActive;

     public AddTokenFilter(String profilesActive) {

         super ();

         this .profilesActive = profilesActive;

     }

     @Override

     public void init(FilterConfig filterConfig) throws ServletException {

     }

     @Override

     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

      //判断是开发模式(dev)还是生产坏境(pro)

      //如果不是开发坏境,不做任何操作,是开发坏境,往本地测试的request加请求头

         if (profilesActive == null || !EnumEnvType.DEV.toString().equalsIgnoreCase(profilesActive)) {

             filterChain.doFilter(servletRequest, servletResponse);

             return ;

         }

         filterChain.doFilter( new CustomeizedRequest((HttpServletRequest) servletRequest), servletResponse);

     }

     @Override

     public void destroy() {

     }

//继承HttpServletRequestWrapper ,重写getHeader获取请求头的值

     private class CustomeizedRequest extends HttpServletRequestWrapper {

         /**

          * Constructs a request object wrapping the given request.

          *

          * @param request

          * @throws IllegalArgumentException if the request is null

          */

         public CustomeizedRequest(HttpServletRequest request) {

             super (request);

         }

         @Override

         public String getHeader(String name) {

             if (!Constant.HTTP_HEADER_ACCESS_TOKEN.equalsIgnoreCase(name)) {

                 return super .getHeader(name);

             }

             String token = super .getHeader(name);

             return StringUtils.isNotBlank(token) ? token : DEFAULT_TOKEN;

         }

     }

}

使用这个Filter很简单,新建一个WebMvcConfig类,配置一个bean

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

import com.alibaba.fastjson.serializer.SerializerFeature;

import com.alibaba.fastjson.support.config.FastJsonConfig;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;

import com.uhope.rl.watersource.filter.AddTokenFilter;

import com.uhope.rl.watersource.filter.ServiceFilter;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.boot.autoconfigure.web.HttpMessageConverters;

import org.springframework.boot.web.servlet.FilterRegistrationBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.http.converter.HttpMessageConverter;

import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.nio.charset.Charset;

/**

  * Spring MVC 配置

  * @author chenbin on 2017/12/25

  * @version 3.0.0

  */

@Configuration

public class WebMvcConfig extends WebMvcConfigurerAdapter {

     private final Logger logger = LoggerFactory.getLogger(WebMvcConfig. class );

     /**

      * 当前激活的配置文件

      */

     @Value ( "${spring.profiles.active}" )

     private String env;

     /**

      * 解决路径资源映射问题

      *

      * @param registry

      */

     @Override

     public void addResourceHandlers(ResourceHandlerRegistry registry) {

         registry.addResourceHandler( "swagger-ui.html" ).addResourceLocations( "classpath:/META-INF/resources/" );

         registry.addResourceHandler( "/webjars/**" ).addResourceLocations( "classpath:/META-INF/resources/webjars/" );

         registry.addResourceHandler( "/static/**" ).addResourceLocations( "classpath:/static/" );

     }

     /**

      * 使用fastJson代替Jackjson解析JSON数据

      *

      * @return

      */

     @Bean

     public HttpMessageConverters fastJsonHttpMessageConverters() {

         FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();

         FastJsonConfig fastJsonConfig = new FastJsonConfig();

         /*

          * 转换为JSON字符串,默认:

          * WriteNullListAsEmpty    List字段如果为null,输出为[],而非null

          * WriteNullStringAsEmpty  字符类型字段如果为null,输出为][,而非null

          * WriteMapNullValue       是否输出值为null的字段,默认为false

          */

         fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);

         fastConverter.setFastJsonConfig(fastJsonConfig);

         fastConverter.setDefaultCharset(Charset.forName( "UTF-8" ));

         HttpMessageConverter<?> converter = fastConverter;

         return new HttpMessageConverters(converter);

     }

     /**

      * 这个Filter 解决页面跨域访问问题

      */

     @Bean

     public FilterRegistrationBean omsFilter() {

         FilterRegistrationBean registration = new FilterRegistrationBean();

         registration.setFilter( new ServiceFilter());

         registration.addUrlPatterns( "/*" );

         registration.setName( "MainFilter" );

         registration.setAsyncSupported( true );

         registration.setOrder( 1 );

         return registration;

     }

     /**

      * 这个Filter 添加token

      */

     @Bean

     public FilterRegistrationBean addTokenFilter(){

         FilterRegistrationBean registration = new FilterRegistrationBean();

         registration.setFilter( new AddTokenFilter(env));

         registration.addUrlPatterns( "/*" );

         registration.setName( "addTokenFilter" );

         registration.setAsyncSupported( true );

         registration.setOrder( 2 );

         return registration;

     }

}

小结一下

这样就实现了开发坏境下添加本地测试的token,若不是开发坏境,用网页请求过来的token,很方便,也解决了Feign丢失请求头的问题

Feign调用进行token鉴权

1、项目场景

这边使用 两个springboot应用,中间通过feign来进行远程调用(是的没错,架构就是这么奇葩)。然后在调用feign的时候,希望可以进行token鉴权。

2、解决办法

请求进来时,通过拦截器,校验header的token,然后在业务中调用feignClient时,通过新加一个feign拦截器,拦截feign请求,把当前的header中的token添加到feign的请求头中去。实现token在链路中的传递。

3、具体实现

新增 feign 拦截器配置

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/**

  * Feign请求拦截器配置.

  *

  * @author linzp

  * @version 1.0.0

  * @date 2021/4/16 21:19

  */

@Configuration

public class FeignInterceptorConfig implements RequestInterceptor {

     public FeignInterceptorConfig() {}

     @Override

     public void apply(RequestTemplate template) {

         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

         HttpServletRequest request = attributes.getRequest();

         //设置token到请求头

         template.header(ConstantCommon.HEADER_TOKEN_KEY, request.getHeader(ConstantCommon.HEADER_TOKEN_KEY));

     }

}

然后在feignClient接口中,添加 == configuration = FeignInterceptorConfig.class==

注意有Bug!!!

注意!!!,这里会有个异常,获取到的request会是null。原因是hytrix隔离策略是thread,无法读到 threadLocal变量。

解决办法!!更改策略

在配置文件中新增如下配置,即可解决!

?

1

2

3

4

5

6

7

# 更换hystrix策略,解决无法传递threadLocal变量问题

hystrix:

     command:

         default :

             execution:

                 isolation:

                     strategy: SEMAPHORE

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://blog.csdn.net/m0_37701381/article/details/82023076

查看更多关于Spring Boot Feign服务调用之间带token问题的详细内容...

  阅读:15次