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請(qǐng)求的時(shí)候沒有帶上token
解決方式
要解決這個(gè)問題,想必能猜到最方便的就是往請(qǐng)求頭里加上token,一起帶過去
Feign有提供一個(gè)接口,RequestInterceptor
只要實(shí)現(xiàn)這個(gè)接口,簡(jiǎn)單做一些處理,比如說(shuō)我們驗(yàn)證請(qǐng)求頭的token叫Access-Token,我們就先取出當(dāng)前請(qǐng)求的token,然后放到feign請(qǐng)求頭上
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請(qǐng)求頭里加上了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; /** * 每次請(qǐng)求過濾器攔截加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ā)壞境,往本地測(cè)試的request加請(qǐng)求頭 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獲取請(qǐng)求頭的值 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很簡(jiǎn)單,新建一個(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 解決頁(yè)面跨域訪問問題 */ @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ā)壞境下添加本地測(cè)試的token,若不是開發(fā)壞境,用網(wǎng)頁(yè)請(qǐng)求過來(lái)的token,很方便,也解決了Feign丟失請(qǐng)求頭的問題
Feign調(diào)用進(jìn)行token鑒權(quán)
1、項(xiàng)目場(chǎng)景
這邊使用 兩個(gè)springboot應(yīng)用,中間通過feign來(lái)進(jìn)行遠(yuǎn)程調(diào)用(是的沒錯(cuò),架構(gòu)就是這么奇葩)。然后在調(diào)用feign的時(shí)候,希望可以進(jìn)行token鑒權(quán)。
2、解決辦法
請(qǐng)求進(jìn)來(lái)時(shí),通過攔截器,校驗(yàn)header的token,然后在業(yè)務(wù)中調(diào)用feignClient時(shí),通過新加一個(gè)feign攔截器,攔截feign請(qǐng)求,把當(dāng)前的header中的token添加到feign的請(qǐng)求頭中去。實(shí)現(xiàn)token在鏈路中的傳遞。
3、具體實(shí)現(xiàn)
新增 feign 攔截器配置
/** * Feign請(qǐng)求攔截器配置. * * @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到請(qǐng)求頭 template.header(ConstantCommon.HEADER_TOKEN_KEY, request.getHeader(ConstantCommon.HEADER_TOKEN_KEY)); } }
然后在feignClient接口中,添加 == configuration = FeignInterceptorConfig.class==
注意有Bug?。?!
注意?。?!,這里會(huì)有個(gè)異常,獲取到的request會(huì)是null。原因是hytrix隔離策略是thread,無(wú)法讀到 threadLocal變量。
解決辦法??!更改策略
在配置文件中新增如下配置,即可解決!
# 更換hystrix策略,解決無(wú)法傳遞threadLocal變量問題 hystrix: command: default: execution: isolation: strategy: SEMAPHORE
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot整合Mybatis-Plus、Jwt實(shí)現(xiàn)登錄token設(shè)置
Spring Boot整合Mybatis-plus實(shí)現(xiàn)登錄常常需要使用JWT來(lái)生成用戶的token并設(shè)置用戶權(quán)限的攔截器,本文就來(lái)詳細(xì)的介紹一下,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02Servlet服務(wù)端實(shí)現(xiàn)原理詳解
Servlet是Sun公司開發(fā)動(dòng)態(tài)web的一門技術(shù),Sun公司在這些API中提供了一個(gè)接口叫做:Servlet,如果想開發(fā)一個(gè)Servlet程序,只需要完成兩個(gè)小步驟:編寫一個(gè)類,實(shí)現(xiàn)Servlet接口、把開發(fā)好的Java類部署到web服務(wù)器中。但是你了解Servlet實(shí)現(xiàn)的原理嗎2022-07-07java如何根據(jù)提供word模板導(dǎo)出word文檔詳解
在日常的開發(fā)工作中,我們時(shí)常會(huì)遇到導(dǎo)出Word文檔報(bào)表的需求,比如公司的財(cái)務(wù)報(bào)表、醫(yī)院的患者統(tǒng)計(jì)報(bào)表、電商平臺(tái)的銷售報(bào)表等等,這篇文章主要給大家介紹了關(guān)于java如何根據(jù)提供word模板導(dǎo)出word文檔的相關(guān)資料,需要的朋友可以參考下2023-09-09Reactor3 Map與FlatMap的區(qū)別示例詳解
這篇文章主要為大家介紹了Reactor3 Map與FlatMap的區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Spring Cloud 系列之注冊(cè)中心 Eureka詳解
Netflix Eureka 是由 Netflix 開源的一款基于 REST 的服務(wù)發(fā)現(xiàn)組件,包括 Eureka Server 及 Eureka Client。這篇文章主要介紹了Spring Cloud 系列之注冊(cè)中心 Eureka,需要的朋友可以參考下2020-11-11