實(shí)戰(zhàn)!openFeign如何實(shí)現(xiàn)全鏈路JWT令牌信息不丟失?

源 / 文/
先送大家一份福利:
《美團(tuán)技術(shù)年貨.pdf》(2019-2021)
在2022年春節(jié)到來之際,美團(tuán)技術(shù)團(tuán)隊(duì)精選過去3年公眾號(hào)50多篇技術(shù)文章以及 20多篇國際頂會(huì)論文,整理制作成一本厚達(dá)1200多頁的電子書,作為新年禮物贈(zèng)送給大家。
這本電子書內(nèi)容覆蓋算法、前端、后端、數(shù)據(jù)、安全、測試等多個(gè)領(lǐng)域。
希望能對同學(xué)們的工作和學(xué)習(xí)有所幫助。
Code A Better Life

需要該P(yáng)DF文檔,可直接長按掃碼添加好友,回復(fù) 「PDF」 獲取:
(長按掃碼識(shí)別)
今天這篇文章介紹一下JWT令牌如何在微服務(wù)鏈路中保證信息不丟失?官方稱為令牌中繼。
什么是令牌中繼?
令牌中繼通俗的講則是讓令牌在微服務(wù)鏈路調(diào)用中傳遞下去,保證各個(gè)微服務(wù)能夠獲取令牌中的用戶信息。
以下訂單的例子來說,如下圖:
客戶端攜帶令牌請求網(wǎng)關(guān),網(wǎng)關(guān)鑒權(quán)成功后會(huì)將令牌中的用戶信息解析出來放在請求頭中下發(fā)給訂單服務(wù),同樣的,訂單服務(wù)需要將用戶信息傳遞給賬戶服務(wù)獲取該用戶的賬戶信息。
那么問題來了?如何保證網(wǎng)關(guān)服務(wù)->訂單服務(wù)->賬戶服務(wù)這條鏈路中的用戶信息傳遞下去是個(gè)痛點(diǎn)
解決方案
令牌在openFeign調(diào)用過程中是不能自動(dòng)中繼的,因此必須手動(dòng)的將令牌信息傳遞下去。
注意:openFeign在開啟熔斷降級后內(nèi)部調(diào)用開啟了子線程,因此傳統(tǒng)的方案直接在RequestInterceptor中設(shè)置是不可行的。
那么如何保證子線程也能獲取請求頭中的用戶信息呢?
答案是:RequestContextHolder這個(gè)神器。
RequestContextHolder內(nèi)部通過InheritableThreadLocal實(shí)現(xiàn)子線程共享信息。
在FeignCircuitBreakerInvocationHandler這個(gè)類中也是有如下一行代碼:
RequestContextHolder.setRequestAttributes(requestAttributes);
正是使用RequestContextHolder將request的信息保存在其中,因此實(shí)現(xiàn)令牌中繼只需要讀取RequestContextHolder的信息即可。
詳細(xì)代碼如下:
/**
* @author 公眾號(hào):碼猿技術(shù)專欄
* 用于實(shí)現(xiàn)令牌信息中繼
*/@Componentpublic class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
//從RequestContextHolder中獲取HttpServletRequest
HttpServletRequest httpServletRequest = RequestContextUtils.getRequest();
//獲取RequestContextHolder中的信息
Map<String, String> headers = getHeaders(httpServletRequest);
//放入feign的RequestTemplate中
for (Map.Entry<String, String> entry : headers.entrySet()) {
template.header(entry.getKey(), entry.getValue());
}
}
/**
* 獲取原請求頭
*/
private Map<String, String> getHeaders(HttpServletRequest request) {
Map<String, String> map = new LinkedHashMap<>();
Enumeration<String> enumeration = request.getHeaderNames();
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
}
return map;
}
}源碼目錄如下圖:
end


頂級程序員:topcoding
做最好的程序員社區(qū):Java后端開發(fā)、Python、大數(shù)據(jù)、AI
一鍵三連「分享」、「點(diǎn)贊」和「在看」


