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.commons.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