實(shí)戰(zhàn)!openFeign如何實(shí)現(xiàn)全鏈路JWT令牌信息不丟失?
???
今天這篇文章介紹一下JWT令牌如何在微服務(wù)鏈路中保證信息不丟失?官方稱為令牌中繼。
什么是令牌中繼?
令牌中繼通俗的講則是讓令牌在微服務(wù)鏈路調(diào)用中傳遞下去,保證各個(gè)微服務(wù)能夠獲取令牌中的用戶信息。
以下訂單的例子來說,如下圖:
???
下單流程
客戶端攜帶令牌請(qǐng)求網(wǎng)關(guān),網(wǎng)關(guān)鑒權(quán)成功后會(huì)將令牌中的用戶信息解析出來放在請(qǐng)求頭中下發(fā)給訂單服務(wù),同樣的,訂單服務(wù)需要將用戶信息傳遞給賬戶服務(wù)獲取該用戶的賬戶信息。
那么問題來了?如何保證網(wǎng)關(guān)服務(wù)->訂單服務(wù)->賬戶服務(wù)這條鏈路中的用戶信息傳遞下去是個(gè)痛點(diǎn)
解決方案
令牌在openFeign調(diào)用過程中是不能自動(dòng)中繼的,因此必須手動(dòng)的將令牌信息傳遞下去。
注意:openFeign在開啟熔斷降級(jí)后內(nèi)部調(diào)用開啟了子線程,因此傳統(tǒng)的方案直接在RequestInterceptor中設(shè)置是不可行的。
那么如何保證子線程也能獲取請(qǐng)求頭中的用戶信息呢?
答案是: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)令牌信息中繼 */ @Component public 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()); } } /** * 獲取原請(qǐng)求頭 */ 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; } }
源碼目錄如下圖:
???