Spring Cloud OAuth 微服務內部Token傳遞的源碼實現解析

背景分析

Spring Cloud OAuth 微服務內部Token傳遞的源碼實現解析

  • 1.客戶端攜帶認證中心發放的token,請求資源服務器A(Spring Security OAuth 發放Token 源碼解析)
  • 2.客戶端攜帶令牌直接訪問資源服務器,資源服務器通過對token 的校驗 (Spring Cloud OAuth2 資源服務器CheckToken 源碼解析 ) 判斷用戶的合法性,並保存到上下文中
  • 3.A服務接口接收到請求,需要通過Feign或者其他RPC框架調用B服務來組裝返回數據

本文主要來探討第三部 A --> B ,token 自定維護的源碼實現

如何實現token 傳遞

配置OAuth2FeignRequestInterceptor 即可

  • 此類是Feign 的攔截器實現
Spring Cloud OAuth 微服務內部Token傳遞的源碼實現解析

@Bean
@ConditionalOnProperty("security.oauth2.client.client-id")
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext,
OAuth2ProtectedResourceDetails resource,) {
return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resource);
}

源碼解析

  • 獲取上下文中的token ,組裝到請求頭
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
// 給請求增加 token
@Override
public void apply(RequestTemplate template) {
template.header(header, extract(tokenType));
}

protected String extract(String tokenType) {
OAuth2AccessToken accessToken = getToken();
return String.format("%s %s", tokenType, accessToken.getValue());
}
// 從spring security 上下文中獲取token
public OAuth2AccessToken getToken() {
OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
if (accessToken == null || accessToken.isExpired()) {
try {
accessToken = acquireAccessToken();
}
}
return accessToken;
}
}
  • 再來看AccessTokenContextRelay, 上下文token 中轉器.非常簡單從上下文獲取認證信息得到把 token 放到上下文
public class AccessTokenContextRelay {
private OAuth2ClientContext context;
public AccessTokenContextRelay(OAuth2ClientContext context) {
this.context = context;
}

public boolean copyToken() {
if (context.getAccessToken() == null) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (authentication != null) {
Object details = authentication.getDetails();
if (details instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails holder = (OAuth2AuthenticationDetails) details;
String token = holder.getTokenValue();
DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(
token);
String tokenType = holder.getTokenType();
if (tokenType != null) {
accessToken.setTokenType(tokenType);
}
context.setAccessToken(accessToken);
return true;
}
}
}
return false;
}
}
  • 什麼時候執行中轉,oauth2 資源服務器非常簡單暴力,加了個攔截器給轉發。
Spring Cloud OAuth 微服務內部Token傳遞的源碼實現解析


源碼非常簡單

談談spring security oauth 實現的問題

  1. 當請求上線文沒有Token,如果調用feign 會直接,這個OAuth2FeignRequestInterceptor 肯定會報錯,因為上下文copy 失敗
  2. 如果設置線程隔離,這裡也會報錯。導致安全上下問題傳遞不到子線程中。
  3. 強制使用攔截器去處理 token 轉發到這裡上下文,使用的業務場景只有這裡,影響性能高

這三個問題,大家在使用的過程中一定會遇到

自定義OAuth2FeignRequestInterceptor

  • 通過外部條件是否執行token中轉
public void apply(RequestTemplate template) {
Collection<string> fromHeader = template.headers().get(SecurityConstants.FROM);
if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {
return;
}
accessTokenContextRelay.copyToken();
if (oAuth2ClientContext != null
&& oAuth2ClientContext.getAccessToken() != null) {
super.apply(template);

}
}
/<string>
  • 手動調用accessTokenContextRelay的copy,當然需要覆蓋原生oauth 客戶端的配置
Spring Cloud OAuth 微服務內部Token傳遞的源碼實現解析


總結

  • 以上源碼參考個人工作經歷
  • 群712477306歡迎一起來討論交流Spring Cloud並領取相關完整面試資料
Spring Cloud OAuth 微服務內部Token傳遞的源碼實現解析


分享到:


相關文章: