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

背景分析

  • 1.客戶端攜帶認證中心發放的token,請求資源服務器A(Spring Security OAuth 發放Token 源碼解析
  • 2.客戶端攜帶令牌直接訪問資源服務器,資源服務器經過對token 的校驗 ([Spring Cloud OAuth2 資源服務器CheckToken 源碼解析

](https://my.oschina.net/giegie...) 判斷用戶的合法性,並保存到上下文中git

  • 3.A服務接口接收到請求,須要經過Feign或者其餘RPC框架調用B服務來組裝返回數據

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

如何實現token 傳遞

配置OAuth2FeignRequestInterceptor 便可

  • 此類是Feign 的攔截器實現

@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 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 客戶端的配置

總結

歡迎關注咱們得到更多的好玩JavaEE 實踐app

相關文章
相關標籤/搜索