成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring Cloud Feign如何實(shí)現(xiàn)JWT令牌中繼以傳遞認(rèn)證信息

開(kāi)發(fā) 開(kāi)發(fā)工具
令牌中繼(Token Relay)是比較正式的說(shuō)法,說(shuō)白了就是讓Token令牌在服務(wù)間傳遞下去以保證資源服務(wù)器能夠正確地對(duì)調(diào)用方進(jìn)行鑒權(quán)。

[[430170]]

令牌中繼

令牌中繼(Token Relay)是比較正式的說(shuō)法,說(shuō)白了就是讓Token令牌在服務(wù)間傳遞下去以保證資源服務(wù)器能夠正確地對(duì)調(diào)用方進(jìn)行鑒權(quán)。

令牌難道不能在Feign自動(dòng)中繼嗎?

如果我們攜帶Token去訪問(wèn)A服務(wù),A服務(wù)肯定能夠鑒權(quán),但是A服務(wù)又通過(guò)Feign調(diào)用B服務(wù),這時(shí)候A的令牌是無(wú)法直接傳遞給B服務(wù)的。

這里來(lái)簡(jiǎn)單說(shuō)下原因,服務(wù)間的調(diào)用通過(guò)Feign接口來(lái)進(jìn)行。在調(diào)用方通常我們編寫(xiě)類(lèi)似下面的Feign接口:

  1. @FeignClient(name = "foo-service",fallback = FooClient.Fallback.class) 
  2. public interface FooClient { 
  3.     @GetMapping("/foo/bar"
  4.     Rest<Map<String, String>> bar(); 
  5.  
  6.     @Component 
  7.     class Fallback implements FooClient { 
  8.         @Override 
  9.         public Rest<Map<String, String>> bar() { 
  10.             return RestBody.fallback(); 
  11.         } 
  12.     } 

當(dāng)我們調(diào)用Feign接口后,會(huì)通過(guò)動(dòng)態(tài)代理來(lái)生成該接口的代理類(lèi)供我們調(diào)用。如果我們不打開(kāi)熔斷我們可以從Spring Security提供SecurityContext對(duì)象中提取到資源服務(wù)器的認(rèn)證對(duì)象JwtAuthenticationToken,它包含了JWT令牌然后我們可以通過(guò)實(shí)現(xiàn)Feign的攔截器接口RequestInterceptor把Token放在請(qǐng)求頭中,偽代碼如下:

  1. /** 
  2.  * 需要注入Spring IoC 
  3.  **/ 
  4. static class BearerTokenRequestInterceptor implements RequestInterceptor { 
  5.         @Override 
  6.         public void apply(RequestTemplate template) { 
  7.             final String authorization = HttpHeaders.AUTHORIZATION
  8.             Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
  9.              
  10.             if (authentication instanceof JwtAuthenticationToken){ 
  11.                 JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication; 
  12.                 String tokenValue = jwtAuthenticationToken.getToken().getTokenValue(); 
  13.                 template.header(authorization,"Bearer "+tokenValue); 
  14.             } 
  15.         } 
  16.     } 

如果我們不開(kāi)啟熔斷這樣搞問(wèn)題不大,為了防止調(diào)用鏈雪崩服務(wù)熔斷基本沒(méi)有不打開(kāi)的。這時(shí)候從SecurityContextHolder就無(wú)法獲取到Authentication了。因?yàn)檫@時(shí)Feign調(diào)用是在調(diào)用方的調(diào)用線程下又開(kāi)啟了一個(gè)子線程中進(jìn)行的。由于我使用的熔斷組件是Resilience4J,對(duì)應(yīng)的線程源碼在Resilience4JCircuitBreaker中:

  1. Supplier<Future<T>> futureSupplier = () -> executorService.submit(toRun::get); 

SecurityContextHolder保存信息是默認(rèn)是通過(guò)ThreadLocal實(shí)現(xiàn)的,我們都知道這個(gè)是不能跨線程的,而Feign的攔截器這時(shí)恰恰在子線程中,因此開(kāi)啟了熔斷功能(circuitBreaker)的Feign無(wú)法直接進(jìn)行令牌中繼。

熔斷組件有過(guò)時(shí)的Hystrix、Resilience4J、還有阿里的哨兵Sentinel,它們的機(jī)制可能有小小的不同。

實(shí)現(xiàn)令牌中繼

雖然直接不能實(shí)現(xiàn)令牌中繼,但是我從中還是找到了一些信息。在Feign接口代理的處理器FeignCircuitBreakerInvocationHandler中發(fā)現(xiàn)了下面的代碼:

  1. private Supplier<Object> asSupplier(final Method method, final Object[] args) { 
  2.   final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); 
  3.   return () -> { 
  4.    try { 
  5.     RequestContextHolder.setRequestAttributes(requestAttributes); 
  6.     return this.dispatch.get(method).invoke(args); 
  7.    } 
  8.    catch (RuntimeException throwable) { 
  9.     throw throwable; 
  10.    } 
  11.    catch (Throwable throwable) { 
  12.     throw new RuntimeException(throwable); 
  13.    } 
  14.    finally { 
  15.     RequestContextHolder.resetRequestAttributes(); 
  16.    } 
  17.   }; 
  18.  } 

這是Feign代理類(lèi)的執(zhí)行代碼,我們可以看到在執(zhí)行前:

  1. final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); 

這里是獲得調(diào)用線程中請(qǐng)求的信息,包含了ServletHttpRequest、ServletHttpResponse等信息。緊接著又在lambda代碼中把這些信息又Setter了進(jìn)去:

  1. RequestContextHolder.setRequestAttributes(requestAttributes); 

如果這是一個(gè)線程中進(jìn)行的簡(jiǎn)直就是吃飽了撐的,事實(shí)上Supplier返回值是在另一個(gè)線程中執(zhí)行的。這樣做的目的就是為了跨線程保存一些請(qǐng)求的元數(shù)據(jù)。

InheritableThreadLocal

  1. public abstract class RequestContextHolder  { 
  2.   
  3.  private static final ThreadLocal<RequestAttributes> requestAttributesHolder = 
  4.    new NamedThreadLocal<>("Request attributes"); 
  5.  
  6.  private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = 
  7.    new NamedInheritableThreadLocal<>("Request context"); 
  8. // 省略 

RequestContextHolder 維護(hù)了兩個(gè)容器,一個(gè)是不能跨線程的ThreadLocal,一個(gè)是實(shí)現(xiàn)了InheritableThreadLocal的NamedInheritableThreadLocal。InheritableThreadLocal是可以把父線程的數(shù)據(jù)傳遞到子線程的,基于這個(gè)原理RequestContextHolder把調(diào)用方的請(qǐng)求信息帶進(jìn)了子線程,借助于這個(gè)原理就能實(shí)現(xiàn)令牌中繼了。

實(shí)現(xiàn)令牌中繼

把最開(kāi)始的Feign攔截器代碼改動(dòng)了一下就實(shí)現(xiàn)了令牌的中繼:

  1. /** 
  2.  * 令牌中繼 
  3.  */ 
  4. static class BearerTokenRequestInterceptor implements RequestInterceptor { 
  5.     private static final Pattern BEARER_TOKEN_HEADER_PATTERN = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+=*)$"
  6.             Pattern.CASE_INSENSITIVE); 
  7.  
  8.     @Override 
  9.     public void apply(RequestTemplate template) { 
  10.         final String authorization = HttpHeaders.AUTHORIZATION
  11.         ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 
  12.         if (Objects.nonNull(requestAttributes)) { 
  13.             String authorizationHeader = requestAttributes.getRequest().getHeader(HttpHeaders.AUTHORIZATION); 
  14.             Matcher matcher = BEARER_TOKEN_HEADER_PATTERN.matcher(authorizationHeader); 
  15.             if (matcher.matches()) { 
  16.                 // 清除token頭 避免傳染 
  17.                 template.header(authorization); 
  18.                 template.header(authorization, authorizationHeader); 
  19.             } 
  20.         } 
  21.     } 

這樣當(dāng)你調(diào)用FooClient.bar()時(shí),在foo-service中資源服務(wù)器(OAuth2 Resource Server)也可以獲得調(diào)用方的令牌,進(jìn)而獲得用戶(hù)的信息來(lái)處理資源權(quán)限和業(yè)務(wù)。

不要忘記將這個(gè)攔截器注入Spring IoC。

總結(jié)

 

 

 

微服務(wù)令牌中繼是非常重要的,保證了用戶(hù)狀態(tài)在調(diào)用鏈路的傳遞。而且這也是微服務(wù)的難點(diǎn)。今天借助于Feign的一些特性和ThreadLocal的特性實(shí)現(xiàn)了令牌中繼供大家參考。

 

責(zé)任編輯:武曉燕 來(lái)源: 碼農(nóng)小胖哥
相關(guān)推薦

2022-08-15 09:22:12

JWT認(rèn)證系統(tǒng)

2022-01-18 08:12:34

JWT鏈路微服務(wù)

2022-05-25 09:00:00

令牌JWT安全

2022-05-17 10:45:55

項(xiàng)目VueElementUI

2024-09-27 20:00:04

2025-04-22 00:05:00

2011-08-15 09:31:55

2021-12-30 08:13:00

JWT登錄令牌

2017-08-10 11:15:05

Spring Clou微服務(wù)架構(gòu)

2017-08-10 16:14:07

FeignRPC模式

2020-06-30 07:58:39

微服務(wù)Spring BootCloud

2021-12-28 11:13:05

安全認(rèn)證 Spring Boot

2022-08-14 09:00:00

JWT 令牌憑證微服務(wù)

2025-02-18 07:37:21

2023-02-20 10:13:00

灰度發(fā)布實(shí)現(xiàn)

2020-05-25 07:00:00

雙因素認(rèn)證身份認(rèn)證密碼

2021-07-11 12:12:49

.NETJWTjson

2023-03-29 13:06:36

2025-06-09 01:01:00

2023-08-10 08:00:42

令牌限流器計(jì)數(shù)器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲成人福利在线观看 | www.日日操 | 久久1区 | 粉色午夜视频 | 日本中文在线视频 | 超碰成人在线观看 | 91免费观看在线 | 在线观看成人免费视频 | 国产精品中文在线 | 久久国产成人 | 欧洲妇女成人淫片aaa视频 | 国产一区二区三区久久久久久久久 | 一本岛道一二三不卡区 | 久久综合久久久 | 久久伊人免费视频 | 欧美91| 国产精品久久久久一区二区三区 | 欧美日韩在线观看一区 | 亚洲不卡在线视频 | 国产小视频在线 | 亚洲大片一区 | 欧美亚洲国产一区 | 亚洲视频三 | 色黄网站 | 久久久久国产精品 | 日本精品视频在线观看 | 91社区在线观看高清 | 免费精品在线视频 | 精品国产三级 | 亚洲成人精品一区 | 精精国产xxxx视频在线播放7 | 狠狠干天天干 | 欧美成人精品一区二区男人看 | 天天干天天插天天 | 亚洲国产精品久久久久婷婷老年 | 亚洲视频一区二区 | 波多野结衣亚洲 | 久久久av | 综合久久99 | 欧美一级α片 | 成人欧美一区二区三区黑人孕妇 |