Spring Security OAuth2之resource_id配置與驗證

  • 2020 年 7 月 28 日
  • 筆記

一、resource_id的作用

OAuth2認證流程

Spring Security OAuth2 架構上分為Authorization Server認證伺服器和Resource Server資源伺服器。我們可以為每一個Resource Server(一個微服務實例)設置一個resourceid。Authorization Server給client第三方客戶端授權的時候,可以設置這個client可以訪問哪一些Resource Server資源服務,如果沒設置,就是對所有的Resource Server都有訪問許可權。

二、ResourceServer如何設置ResourceID

在每個ResourceServer實例上設置resourceId,該resourceId作為該服務資源的唯一標識。(假如同一個微服務資源部署多份,resourceId相同)

@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {

    private static final String DEMO_RESOURCE_ID = "test-resource";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(DEMO_RESOURCE_ID)
        //...... 還可以有有其他的配置
    }
 }

三、AuthorizationServer如何設置ResourceIDs

在AuthorizationServer為客戶端client配置ResourceID的目的是:限制某個client可以訪問的資源服務。

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    //配置客戶端存儲到db 代替原來得記憶體模式
    JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
    clientDetailsService.setPasswordEncoder(passwordEncoder);
    clients.withClientDetails(clientDetailsService);
}

這裡需要使用JdbcClientDetailsService類和資料庫表oauth_client_details進行配置的持久化存儲,以及動態配置。

oauth_client_details

三、ResourceID在哪驗證

ResourceID當然是在Resource Server資源伺服器進行驗證(你能不能訪問我的資源,當然由我自己來驗證)。當資源請求發送到Resource Server的時候會攜帶access_token,Resource Server會根據access_token找到client_id,進而找到該client可以訪問的resource_ids。如果resource_ids包含ResourceServer自己設置ResourceID,這關就過去了,就可以繼續進行其他的許可權驗證。

  • @EnableResourceServer會給Spring Security的FilterChan添加一個OAuth2AuthenticationProcessingFilter過濾器,過濾所有的資源請求。
  • OAuth2AuthenticationProcessingFilter會使用OAuth2AuthenticationManager來驗證token。驗證Token的時候會去oauth_client_details表載入client配置資訊。

如果AuthorizationServer認證client1可以訪問test-resource,但client1去訪問了oauth-rs,會響應如下資訊:

{"error":"access_denied","error_description":"Invalid token does not contain resource id (oauth-rs)"}

具體實現resource_id驗證的源碼:OAuth2AuthenticationManager#authenticate(Authentication authentication)

public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        if (authentication == null) {
            throw new InvalidTokenException("Invalid token (token not found)");
        }
        String token = (String) authentication.getPrincipal();
        OAuth2Authentication auth = tokenServices.loadAuthentication(token);
        if (auth == null) {
            throw new InvalidTokenException("Invalid token: " + token);
        }

        Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
        if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
            throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
        }

        checkClientDetails(auth);

        if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
            // Guard against a cached copy of the same details
            if (!details.equals(auth.getDetails())) {
                // Preserve the authentication details from the one loaded by token services
                details.setDecodedDetails(auth.getDetails());
            }
        }
        auth.setDetails(authentication.getDetails());
        auth.setAuthenticated(true);
        return auth;

    }

下面這段便是驗證resourceid的地方

Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
    throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
}

在Spring Security的FilterChain中,OAuth2AuthenticationProcessingFilter在FilterSecurityInterceptor的前面,所以會先驗證client有沒有此resource的許可權,只有在有此resource的許可權的情況下,才會再去做進一步的進行其他驗證的判斷。

歡迎關注我的部落格,裡面有很多精品合集

  • 本文轉載註明出處(必須帶連接,不能只轉文字):字母哥部落格

覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力! 。另外,筆者最近一段時間輸出了如下的精品內容,期待您的關注。