Feign服务调服务传递数据带token验证
Feign服务调服务就不多做介绍了,值得提醒的是,Feign服务调服务传递数据的时候,比如某用户服务是需要带token验证的,而调用那个用户服务的时候报错,提示token为空,是因为Feign请求的时候没有带上token
解决方式
要解决这个问题,想必能猜到最方便的就是往请求头里加上token,一起带过去
Feign有提供一个接口,RequestInterceptor
只要实现这个接口,简单做一些处理,比如说我们验证请求头的token叫Access-Token,我们就先取出当前请求的token,然后放到feign请求头上
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;
@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接口
import org.apache.commons.lang3.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
public class AddTokenFilter implements Filter {
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 {
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
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;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(WebMvcConfig.class);
@Value("${spring.profiles.active}")
private String env;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
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);
}
@Bean
public FilterRegistrationBean omsFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new ServiceFilter());
registration.addUrlPatterns("
@Bean
public FilterRegistrationBean addTokenFilter(){
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new AddTokenFilter(env));
registration.addUrlPatterns("
@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变量。
解决办法!!更改策略
在配置文件中新增如下配置,即可解决!
# 更换hystrix策略,解决无法传递threadLocal变量问题
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。