使用Feign設(shè)置Token鑒權(quán)調(diào)用接口
Feign設(shè)置Token鑒權(quán)調(diào)用接口
聲明FeignClient 指定url
/** ?* CREATE BY songzhongjin ON 2021.05.08 15:58 星期六 ?* DESC:feign方式 測(cè)試Deom ?*/ @FeignClient(name = "testService", url = "http://xxxxxx:8088") public interface FeignTest { ? ? /** ? ? ?* 通過(guò)feign調(diào)用接口 ? ? ?* @param map ? ? ?* @return ? ? ?*/ ? ? @PostMapping(value = "/xxxxx/sys/login") ? ? String login(Map<String, Object> map); }
調(diào)用測(cè)試
/** ?* CREATE BY songzhongjin ON 2021.05.08 16:02 星期六 ?* DESC: ?*/ @RestController public class Test { ? ? @Autowired ? ? FeignTest feignTest; ? ? @Autowired ? ? MetaDataService metaDataService; ? ? @PostMapping("/test") ? ? public void test() { ? ? ? ? HashMap<String, Object> map = new HashMap<>(); ? ? ? ? map.put("user_id", "xxx"); ? ? ? ? map.put("password", "xxxxx"); ? ? ? ? //調(diào)用 T 具體對(duì)象具體封裝? ? ? ? ? MetaDataResponseVO<T> login = feignTest.login(map); ? ? ? ? List<T> data = login.getData(); ? ? ? ? System.out.println(login.getData()); ? ? ? ? //處理業(yè)務(wù)data ? ? } }
返回對(duì)象可以封裝demo
@Data public class MetaDataResponseVO<T> implements Serializable { ? ? private static final long serialVersionUID = 316492198399615153L; ? ? /** ? ? ?* 狀態(tài)碼. ? ? ?*/ ? ? private String retcode; ? ? /** ? ? ?* 狀態(tài)碼描述. ? ? ?*/ ? ? private String retmsg; ? ? /** ? ? ?* 響應(yīng)包體. ? ? ?*/ ? ? private List<T> data; }
設(shè)置token 進(jìn)行調(diào)用,F(xiàn)eign 的請(qǐng)求攔截器來(lái)統(tǒng)一添加請(qǐng)求頭信息
先去implements RequestInterceptor 重寫apply方法
/** ?* feign攔截器配置,調(diào)用前先鑒權(quán). ?*/ @Component public class MetaDataFeignConfig implements RequestInterceptor { ??? ?public FeignBasicAuthRequestInterceptor() { ? ? } ? ? /** ? ? ?* 給feign請(qǐng)求加上accessToken請(qǐng)求頭. ? ? ?* ? ? ?* @param template ? ? ?*/ ? ? @Override ? ? public void apply(RequestTemplate template) { ? ? ? ? //feign加請(qǐng)求頭 自定義fangjia.auth.token" ? ? ? ? template.header("access_token", System.getProperty("fangjia.auth.token")); ? ? } }
配置攔截器
攔截器需要在 Feign 的配置中定義,代碼如下所示。
@Configuration public class FeignConfiguration { ? ? /** ? ? ?* 日志級(jí)別 ? ? ?* ? ? ?* @return ? ? ?*/ ? ? @Bean ? ? Logger.Level feignLoggerLevel() { ? ? ? ? return Logger.Level.FULL; ? ? } ? ? /** ? ? ?* 創(chuàng)建 Feign 請(qǐng)求攔截器, 在發(fā)送請(qǐng)求前設(shè)置認(rèn)證的 Token, 各個(gè)微服務(wù)將 Token 設(shè)置 到環(huán)境變量中來(lái)達(dá)到通用的目的 ? ? ?* ? ? ?* @return ? ? ?*/ ? ? @Bean ? ? public FeignBasicAuthRequestInterceptor basicAuthRequestInterceptor() { ? ? ? ? return new FeignBasicAuthRequestInterceptor(); ? ? } }
上面的準(zhǔn)備好之后,我們只需要在調(diào)用業(yè)務(wù)接口之前先調(diào)用認(rèn)證接口,然后將獲取到的 Token 設(shè)置到環(huán)境變量中,通過(guò) System.setProperty(“fangjia.auth.token”,token) 設(shè)置值,可以使用redis存放避免每次調(diào)用。
? ? @Value("${feign-client.meta-data.user}") ? ? private String userId; ? ? @Value("${feign-client.meta-data.password}") ? ? private String password; ? ? private static final String METADATA_ACCESS_TOKEN = "metaDataAccessToken"; ? ? /** ? ? ?* 獲取token,設(shè)置到上下文. ? ? ?*/ ? ? public void signInMetaData() { ? ? ? ? //拿緩存 ? ? ? ? String accessToken = redisUtils.get(METADATA_ACCESS_TOKEN); ? ? ? ? log.warn("-----------從redis拿meta的token結(jié)果--token ={}-------------", accessToken); ? ? ? ? //System.setProperty("fangjia.metadata.token",token) 設(shè)置token值 ? ? ? ? System.setProperty("feign.metadata.token", accessToken); ? ? ? ? log.warn("--------------設(shè)置metaData接口鑒權(quán)結(jié)束-----------------"); ? ? }
token設(shè)置完成,我們需要在我們其他的feign文件中配置這個(gè)token,
注意配置對(duì)應(yīng)的攔截器configuration ,MetaDataFeignConfig.class這個(gè)類就是我們?cè)O(shè)置頭信息的
@FeignClient(name = “metaDataClient”, url = “${feign-client.meta-data.url}”, configuration = MetaDataFeignConfig.class)
package com.infinitus.dmm.openapi; /** ?* 無(wú)限極元數(shù)據(jù)接口. ?* ?* @author 林志鵬 ?* @date 2021/5/7 ?*/ @FeignClient(name = "metaDataClient", url = "${feign-client.meta-data.url}", configuration = MetaDataFeignConfig.class) public interface MetaDataClient { ? ? /** ? ? ?* 拉取物理系統(tǒng)列表. ? ? ?*/ ? ? @RequestMapping(value = "/sc/mtdsystemlist", method = RequestMethod.GET) ? ? MetaDataResponseVO<MetaDataSystem> pullPhysicalSystemList();
我們?cè)谡{(diào)用該業(yè)務(wù)接口時(shí)候,需要先去調(diào)用設(shè)置頭信息feign,在調(diào)用業(yè)務(wù)feign。
?/** ? ? ?* 拉取物理系統(tǒng)列表. ? ? ?* ? ? ?* @return ? ? ?*/ ? ? public List<MetaDataSystem> pullPhysicalSystem() { ? ? ? ? //鑒權(quán)feign ? ? ? ? signInMetaData(); ? ? ? ? //業(yè)務(wù)feign ? ? ? ? MetaDataResponseVO<MetaDataSystem> responseVO = metaDataClient.pullPhysicalSystemList(); ? ? ? ? if (Objects.nonNull(responseVO)) { ? ? ? ? ? ? List<MetaDataSystem> data = responseVO.getData(); ? ? ? ? ? ? if (Objects.nonNull(data) && !data.isEmpty()) { ? ? ? ? ? ? ? ? return data; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return new ArrayList<>(); ? ? }
補(bǔ)充
經(jīng)過(guò)測(cè)試 鑒權(quán)接口調(diào)用成功,但是業(yè)務(wù)接口返回竟然超過(guò)10s feign默認(rèn)的返回1秒就會(huì)觸發(fā)熔斷機(jī)制,所以我們需要設(shè)置feign的超時(shí)時(shí)間,可以指定FeignClient 名name 很人性化。
@FeignClient(name = “metaDataClient”, url = “${feign-client.meta-data.url}”)
#給metaDataClient服務(wù)設(shè)置超時(shí)時(shí)間 這里metaDataClient是我自己,全局的話metaDataClient替換default feign: ? client: ? ? config: ? ? ? metaDataClient: ? ? ? ? connect-timeout: 50000 ? ? ? ? read-timeout: 50000 ? hystrix: ? ? enabled: false
Feign調(diào)用進(jìn)行Token鑒權(quán)
項(xiàng)目場(chǎng)景
這邊使用 兩個(gè)springboot應(yīng)用,中間通過(guò)feign來(lái)進(jìn)行遠(yuǎn)程調(diào)用(是的沒(méi)錯(cuò),架構(gòu)就是這么奇葩)。然后在調(diào)用feign的時(shí)候,希望可以進(jìn)行token鑒權(quán)。
解決辦法
請(qǐng)求進(jìn)來(lái)時(shí),通過(guò)攔截器,校驗(yàn)header的token,然后在業(yè)務(wù)中調(diào)用feignClient時(shí),通過(guò)新加一個(gè)feign攔截器,攔截feign請(qǐng)求,把當(dāng)前的header中的token添加到feign的請(qǐng)求頭中去。實(shí)現(xiàn)token在鏈路中的傳遞。
具體實(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變量問(wèn)題 hystrix: ? ? command: ? ? ? ? default: ? ? ? ? ? ? execution: ? ? ? ? ? ? ? ? isolation: ? ? ? ? ? ? ? ? ? ? strategy: SEMAPHORE
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java8中Optional的一些常見(jiàn)錯(cuò)誤用法總結(jié)
我們知道 Java 8 增加了一些很有用的 API, 其中一個(gè)就是 Optional,下面這篇文章主要給大家介紹了關(guān)于Java8中Optional的一些常見(jiàn)錯(cuò)誤用法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-07-07Spring?boot?啟動(dòng)流程及外部化配置方法
平時(shí)我們開(kāi)發(fā)Spring boot 項(xiàng)目的時(shí)候,一個(gè)SpringBootApplication注解加一個(gè)main方法就可以啟動(dòng)服務(wù)器運(yùn)行起來(lái),那它到底是怎么運(yùn)行起來(lái)的呢?這篇文章主要介紹了Spring?boot?啟動(dòng)流程及外部化配置,需要的朋友可以參考下2022-12-12Java實(shí)現(xiàn)PDF文件的分割與加密功能
這篇文章主要為大家分享了如何利用Java語(yǔ)言實(shí)現(xiàn)PDF文件的分割與加密以及封面圖的生成,文中的示例代碼簡(jiǎn)潔易懂,感興趣的可以了解一下2022-04-04SpringBoot中使用Cookie實(shí)現(xiàn)記住登錄的示例代碼
這篇文章主要介紹了SpringBoot中使用Cookie實(shí)現(xiàn)記住登錄的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07selenium高效應(yīng)對(duì)Web頁(yè)面元素刷新的實(shí)例講解
今天小編就為大家分享一篇selenium高效應(yīng)對(duì)Web頁(yè)面元素刷新的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05DynamicDataSource怎樣解決多數(shù)據(jù)源的事務(wù)問(wèn)題
這篇文章主要介紹了DynamicDataSource怎樣解決多數(shù)據(jù)源的事務(wù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07springboot中請(qǐng)求地址轉(zhuǎn)發(fā)的兩種方案
在開(kāi)發(fā)過(guò)程中,我們經(jīng)常需要將請(qǐng)求從一個(gè)服務(wù)轉(zhuǎn)發(fā)到另一個(gè)服務(wù),以實(shí)現(xiàn)不同服務(wù)之間的協(xié)作,本文主要介紹了springboot中請(qǐng)求地址轉(zhuǎn)發(fā)的兩種方案,感興趣的可以了解一下2023-11-11