Spring Boot Feign服務(wù)調(diào)用之間帶token問題
Feign服務(wù)調(diào)服務(wù)傳遞數(shù)據(jù)帶token驗(yàn)證
Feign服務(wù)調(diào)服務(wù)就不多做介紹了,值得提醒的是,F(xiàn)eign服務(wù)調(diào)服務(wù)傳遞數(shù)據(jù)的時(shí)候,比如某用戶服務(wù)是需要帶token驗(yàn)證的,而調(diào)用那個(gè)用戶服務(wù)的時(shí)候報(bào)錯(cuò),提示token為空,是因?yàn)镕eign請求的時(shí)候沒有帶上token
解決方式
要解決這個(gè)問題,想必能猜到最方便的就是往請求頭里加上token,一起帶過去
Feign有提供一個(gè)接口,RequestInterceptor
只要實(shí)現(xiàn)這個(gè)接口,簡單做一些處理,比如說我們驗(yàn)證請求頭的token叫Access-Token,我們就先取出當(dāng)前請求的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;
/**
* Feign配置
* 使用FeignClient進(jìn)行服務(wù)間調(diào)用,傳遞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"));
}
}
這樣已經(jīng)成功往Feign請求頭里加上了Token,還可以這樣,為了方便本地調(diào)試,可以在Spring Boot加上過濾器,每次本地調(diào)用沒有Token的時(shí)候加上一個(gè),只要實(shí)現(xiàn)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;
/**
* 每次請求過濾器攔截加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 {
//判斷是開發(fā)模式(dev)還是生產(chǎn)壞境(pro)
//如果不是開發(fā)壞境,不做任何操作,是開發(fā)壞境,往本地測試的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;
}
}
}
使用這個(gè)Filter很簡單,新建一個(gè)WebMvcConfig類,配置一個(gè)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;
/**
* 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);
/**
* 當(dāng)前激活的配置文件
*/
@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數(shù)據(jù)
*
* @return
*/
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
/*
* 轉(zhuǎn)換為JSON字符串,默認(rèn):
* WriteNullListAsEmpty List字段如果為null,輸出為[],而非null
* WriteNullStringAsEmpty 字符類型字段如果為null,輸出為”“,而非null
* WriteMapNullValue 是否輸出值為null的字段,默認(rèn)為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);
}
/**
* 這個(gè)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;
}
/**
* 這個(gè)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;
}
}
小結(jié)一下
這樣就實(shí)現(xiàn)了開發(fā)壞境下添加本地測試的token,若不是開發(fā)壞境,用網(wǎng)頁請求過來的token,很方便,也解決了Feign丟失請求頭的問題
Feign調(diào)用進(jìn)行token鑒權(quán)
1、項(xiàng)目場景
這邊使用 兩個(gè)springboot應(yīng)用,中間通過feign來進(jìn)行遠(yuǎn)程調(diào)用(是的沒錯(cuò),架構(gòu)就是這么奇葩)。然后在調(diào)用feign的時(shí)候,希望可以進(jìn)行token鑒權(quán)。
2、解決辦法
請求進(jìn)來時(shí),通過攔截器,校驗(yàn)header的token,然后在業(yè)務(wù)中調(diào)用feignClient時(shí),通過新加一個(gè)feign攔截器,攔截feign請求,把當(dāng)前的header中的token添加到feign的請求頭中去。實(shí)現(xiàn)token在鏈路中的傳遞。
3、具體實(shí)現(xiàn)
新增 feign 攔截器配置
/**
* 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();
//設(shè)置token到請求頭
template.header(ConstantCommon.HEADER_TOKEN_KEY, request.getHeader(ConstantCommon.HEADER_TOKEN_KEY));
}
}
然后在feignClient接口中,添加 == configuration = FeignInterceptorConfig.class==
注意有Bug?。?!
注意?。?!,這里會(huì)有個(gè)異常,獲取到的request會(huì)是null。原因是hytrix隔離策略是thread,無法讀到 threadLocal變量。
解決辦法??!更改策略
在配置文件中新增如下配置,即可解決!
# 更換hystrix策略,解決無法傳遞threadLocal變量問題
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java詳細(xì)講解Math和Random類中有哪些常用方法
Math類位于java.lang包中,包含很多用于科學(xué)計(jì)算的類方法,這些方法可以直接通過類名調(diào)用。Random類獲取隨機(jī)數(shù),位于java.util包中,本篇帶你了解它們的常用方法2022-05-05
java線程并發(fā)blockingqueue類使用示例
BlockingQueue是一種特殊的Queue,若BlockingQueue是空的,從BlockingQueue取東西的操作將會(huì)被阻斷進(jìn)入等待狀態(tài)直到BlocingkQueue進(jìn)了新貨才會(huì)被喚醒,下面是用BlockingQueue來實(shí)現(xiàn)Producer和Consumer的例子2014-01-01
java多態(tài)實(shí)現(xiàn)電子寵物系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java多態(tài)實(shí)現(xiàn)電子寵物系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
Spring Boot使用過濾器和攔截器分別實(shí)現(xiàn)REST接口簡易安全認(rèn)證示例代碼詳解
這篇文章主要介紹了Spring Boot使用過濾器和攔截器分別實(shí)現(xiàn)REST接口簡易安全認(rèn)證示例代碼,通過開發(fā)實(shí)踐,理解過濾器和攔截器的工作原理,需要的朋友可以參考下2018-06-06

