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

冷冷gg發表於2019-04-17

背景分析

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

本文主要來探討第三部 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);
	}
}
複製程式碼
  • 手動呼叫accessTokenContextRelay的copy,當然需要覆蓋原生oauth 客戶端的配置
    Spring Cloud OAuth 微服務內部Token傳遞的原始碼實現解析

總結

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

歡迎關注我們獲得更多的好玩JavaEE 實踐

相關文章