實戰!openFeign如何實現全鏈路JWT令牌信息不丟失?
???
今天這篇文章介紹一下JWT令牌如何在微服務鏈路中保證信息不丟失?官方稱為令牌中繼。
什么是令牌中繼?
令牌中繼通俗的講則是讓令牌在微服務鏈路調用中傳遞下去,保證各個微服務能夠獲取令牌中的用戶信息。
以下訂單的例子來說,如下圖:
???
下單流程
客戶端攜帶令牌請求網關,網關鑒權成功后會將令牌中的用戶信息解析出來放在請求頭中下發給訂單服務,同樣的,訂單服務需要將用戶信息傳遞給賬戶服務獲取該用戶的賬戶信息。
那么問題來了?如何保證網關服務->訂單服務->賬戶服務這條鏈路中的用戶信息傳遞下去是個痛點
解決方案
令牌在openFeign調用過程中是不能自動中繼的,因此必須手動的將令牌信息傳遞下去。
注意:openFeign在開啟熔斷降級后內部調用開啟了子線程,因此傳統的方案直接在RequestInterceptor中設置是不可行的。
那么如何保證子線程也能獲取請求頭中的用戶信息呢?
答案是:RequestContextHolder這個神器。
RequestContextHolder內部通過InheritableThreadLocal實現子線程共享信息。
在FeignCircuitBreakerInvocationHandler這個類中也是有如下一行代碼:
RequestContextHolder.setRequestAttributes(requestAttributes);
正是使用RequestContextHolder將request的信息保存在其中,因此實現令牌中繼只需要讀取RequestContextHolder的信息即可。
詳細代碼如下:
/** * @author 公眾號:碼猿技術專欄 * 用于實現令牌信息中繼 */ @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()); } } /** * 獲取原請求頭 */ 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; } }
源碼目錄如下圖:
???