Spring Security OAuth 2.0


·前一篇《OAuth 2.0

OAuth 2.0 Provider 實現

在OAuth 2.0中,provider角色事實上是把授權服務和資源服務分開,有時候它們也可能在同一個應用中,用Spring Security OAuth你可以選擇把它們分成兩個應用,當然多個資源服務可以共享同一個授權服務。

獲取token的請求由Spring MVC的控制端點處理,訪問受保護的資源由標准的Spring Security請求過濾器處理。

(The requests for the tokens are handled by Spring MVC controller endpoints, and access to protected resources is handled by standard Spring Security request filters. )

為了實現OAuth 2.0授權服務器,在Spring Security的過濾器鏈中需要有以下端點:

  • AuthorizationEndpoint    用於服務授權請求。默認URL/oauth/authorize
  • TokenEndpoint    用於服務訪問令牌請求。默認URL/oauth/token

OAuth 2.0的資源服務器中需要實現下列過濾器:

  • OAuth2AuthenticationProcessingFilter    用於加載認證

對於所有的OAuth 2.0 provider特性,最簡單的配置是用Spring OAuth @Configuration適配器。

Authorization Server 配置

只要你配置了授權服務器,那么你應該考慮客戶端用於獲取access token的授權類型(例如,授權碼,用戶憑證,刷新token)。

服務器的配置是用來提供client detail服務和token服務的,並且可以啟用或者禁用全局的某些機制。

每個客戶端可以配置不同的權限

@EnableAuthorizationServer注解被用來配置授權服務器,也可以和實現了AuthorizationServerConfigurer接口的任意被標記為@Bean的Bean一起來對授權服務器進行配置。

下列特性被委托給AuthorizationServerConfigurer:

  • ClientDetailsServiceConfigurer  :a configurer that defines the client details service
  • AuthorizationServerSecurityConfigurer  :defines the security constraints on the token endpoint
  • AuthorizationServerEndpointsConfigurer  :defines the authorization and token endpoints and the token services

一件重要的事情是,provider配置了將授權碼給OAuth客戶端的方式(PS:在授權碼類型授權過程中)

OAuth客戶端通過將end-user(最終用戶)導向授權頁,用戶可用在此輸入他的憑證。之后,授權服務器攜帶授權碼通過重定向的方式將授權碼返回給客戶端。

配置 Client Details

The ClientDetailsServiceConfigurer (a callback from your AuthorizationServerConfigurer) can be used to define an in-memory or JDBC implementation of the client details service.

ClientDetailsServiceConfigurer可用使用client details service的兩種實現中的任意一種:in-memory 或者 JDBC 

客戶端重要的屬性是:

  • clientId  :(必須的)客戶端ID
  • secret  :(對於信任的客戶端需要)客戶端秘鑰
  • scope  :客戶端被限定的范圍。如果scope為定義或者為空(默認為空)則客戶端不受scope限制
  • authorizedGrantTypes  :客戶端使用到的授權類型
  • authorities  :授予客戶端的權限

客戶端details可以在應用運行時被更新,通過直接訪問存儲(例如:如果用JdbcClientDetailsService的話可以實時改變數據庫表中的數據)或者通過實現ClientDetailsManager接口(它們也都實現了ClientDetailsService接口)。

NOTE: the schema for the JDBC service is not packaged with the library (because there are too many variations you might like to use in practice), but there is an example you can start from in the test code in github.

注意:用於JDBC服務的數據庫schema並沒有打包到library中(因為你再實際使用的時候可能有諸多差異),但是這里有一個例子你可以參考一下。

https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

 1 -- used in tests that use HSQL
 2 create table oauth_client_details (
 3     client_id VARCHAR(256) PRIMARY KEY,
 4     resource_ids VARCHAR(256),
 5     client_secret VARCHAR(256),
 6     scope VARCHAR(256),
 7     authorized_grant_types VARCHAR(256),
 8     web_server_redirect_uri VARCHAR(256),
 9     authorities VARCHAR(256),
10     access_token_validity INTEGER,
11     refresh_token_validity INTEGER,
12     additional_information VARCHAR(4096),
13     autoapprove VARCHAR(256)
14 );
15 
16 create table oauth_client_token (
17     token_id VARCHAR(256),
18     token LONGVARBINARY,
19     authentication_id VARCHAR(256) PRIMARY KEY,
20     user_name VARCHAR(256),
21     client_id VARCHAR(256)
22 );
23 
24 create table oauth_access_token (
25     token_id VARCHAR(256),
26     token LONGVARBINARY,
27     authentication_id VARCHAR(256) PRIMARY KEY,
28     user_name VARCHAR(256),
29     client_id VARCHAR(256),
30     authentication LONGVARBINARY,
31     refresh_token VARCHAR(256)
32 );
33 
34 create table oauth_refresh_token (
35     token_id VARCHAR(256),
36     token LONGVARBINARY,
37     authentication LONGVARBINARY
38 );
39 
40 create table oauth_code (
41     code VARCHAR(256), authentication LONGVARBINARY
42 );
43 
44 create table oauth_approvals (
45     userId VARCHAR(256),
46     clientId VARCHAR(256),
47     scope VARCHAR(256),
48     status VARCHAR(10),
49     expiresAt TIMESTAMP,
50     lastModifiedAt TIMESTAMP
51 );
52 
53 
54 -- customized oauth_client_details table
55 create table ClientDetails (
56     appId VARCHAR(256) PRIMARY KEY,
57     resourceIds VARCHAR(256),
58     appSecret VARCHAR(256),
59     scope VARCHAR(256),
60     grantTypes VARCHAR(256),
61     redirectUrl VARCHAR(256),
62     authorities VARCHAR(256),
63     access_token_validity INTEGER,
64     refresh_token_validity INTEGER,
65     additionalInformation VARCHAR(4096),
66     autoApproveScopes VARCHAR(256)
67 );

管理Tokens

AuthorizationServerTokenServices定義了管理OAuth 2.0 Token所必須的操作。請注意:

  • 當創建一個access token的時候,這個認證必須被存儲起來,以便后續訪問資源的時候對接收到的access token進行引用校驗。
  • access token用來加載認證

當你實現了AuthorizationServerTokenServices接口,你可能考慮用DefaultTokenServices。有許多內置的插件化的策略可以用來改變access token的格式和存儲。

默認情況下,用隨機值來生成token,並且用TokenService來處理所有(除了token持久化以外)事情。默認的存儲是in-memory實現,但是有其它的實現可以使用。

  • 對於單服務器而言,默認的InMemoryTokenStore是完美的。大多數的項目是從這里開始的,為了使它很容易啟動,也不需要其它依賴,並且可能以開發模式進行操作。
  • JdbcTokenStore是JDBC版本的Token存儲。它把Token數據存儲到關系型數據庫中。為了使用JdbcTokenStore需要classpath下有"spring-jdbc"。
  • JSON Web Token (JWT) 它將授權的token的所有數據進行編碼后存儲(沒有使用后端存儲是它最大的優勢)。這種方式的一個缺點是你不能很容易的撤銷一個access token,因此一般用該方式存儲的token的有效期很短,並且在刷新token的時候之前的token會被廢除。另一個缺點是,token很長,因為它里面存了很多關於用戶憑證的信息。JwtTokenStore不會真的存儲數據,它不持久化任何數據。但是在DefaultTokenServices中,它扮演着token值和認證信息轉換的角色。

注意:對於JDBC的schema沒有打包到library中,但是這兒有一個例子你可以參考一下test code in github。確保用@EnableTransactionManagement來防止多個客戶端在同一行創建token。注意,示例中的schema都有明確地主鍵聲明,在並發環境中這是必須的。

JWT Tokens

為了使用JWT Tokens,你需要在你的授權服務器中有一個JwtTokenStore。資源服務器也需要解碼這個token,所以JwtTokenStore有一個依賴JwtAccessTokenConverter,相同的實現需要被包含在授權服務器和資源服務器中。也就是說,授權服務器和資源服務器中都需要JwtTokenStore實現。默認情況下,token是被簽名的,而且資源服務器必須能夠校驗這個簽名,因此需要有相同的對稱key,或者需要公鑰來匹配授權服務器上的私鑰。公鑰被授權服務器暴露在/oauth/token_key端點,默認情況下這個端點的訪問規則是"denyAll()"。你可以用標准的SpEL表達式(例如:permitAll())到AuthorizationServerSecurityConfigurer來開放它。

為了使用JwtTokenStore,在classpath下需要有"spring-security-jwt"

Grant Types

授權類型通過AuthorizationEndpoint來支持。默認情況下,除了password以外,所有授權類型都支持。下面是授權類型的一些屬性:

  • authenticationManager  :通過注入一個AuthenticationManager來切換成password授權
  • userDetailsService  :如果你注入一個UserDetailsService或者以任意方式配置了一個全局的UserDetailsService(例如:在GlobalAuthenticationManagerConfigurer中),那么一個刷新token將被包含在user detail中,為了強制賬戶是激活的。
  • authorizationCodeServices  :定義授權碼服務(AuthorizationCodeServices的實例)
  • implicitGrantService  :在隱式授權期間管理狀態
  • tokenGranter  :tokenGranter

Configuring the Endpoint URLs

AuthorizationServerEndpointsConfigurer有一個pathMapping()方法。它有兩個參數:

  • 端點的默認URL路徑
  • 自定義的路徑(必須以"/"開頭)

下面是框架提供的URL路徑:

  • /oauth/authorize    授權端點
  • /oauth/token    令牌端點
  • /oauth/confirm_access    用戶批准授權的端點
  • /oauth/error    用於渲染授權服務器的錯誤
  • /oauth/check_token    資源服務器解碼access token
  • /oauth/check_token    當使用JWT的時候,暴露公鑰的端點

授權端點/oauth/authorize應該被保護起來,以至於它只能被認證過的用戶訪問。下面是一個例子,用標准的Spring Security WebSecurityConfigurer

   @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests().antMatchers("/login").permitAll().and()
        // default protection for all resources (including /oauth/authorize)
            .authorizeRequests()
                .anyRequest().hasRole("USER")
        // ... more configuration, e.g. for form login
    }

注意:如果您的授權服務器同時也是一個資源服務器的話,那么就有另一個具有較低優先級的安全過濾器鏈來控制API資源。通過訪問令牌來保護這些請求,你需要它們的路徑不能與主用戶過濾器鏈中的那些相匹配,所以請確保包含一個請求matcher,它只挑選出上面的WebSecurityConfigurer中的非api資源。

Customizing the UI

授權服務器的大多數端點主要都是被機器使用的,但是有兩個資源是需要UI,它們分別是/oauth/confirm_access和HTML響應/oauth/error。框架為它們提供的實現是空白頁,真實的情況是大多數授權服務器可能想要提供它們自己的實現來控制樣式和內容。所以,你需要做的事情就是提供一個Spring MVC 被標注了@RequestMappings注解的Controller來映射這些端點,並且框架將用一個低的優先級來發放請求。在默認的/oauth/confirm_access你期望一個AuthorizationRequest綁定到session。你可以抓取請求的所有數據並按照自己喜歡的方式渲染它們,然后用戶需要做的就是向/oauth/authorize發送關於批准或拒絕授予的信息。默認的UserApprovalHandler取決於是否你再AuthorizationServerEndpointsConfigurer中提供了一個ApprovalStore。標准的審批處理器如下:

  • TokenStoreUserApprovalHandler  :通過user_oauth_approval做一個簡單的yes/no決定等同於“true”或“false”
  • ApprovalStoreUserApprovalHandler  :一組"scope*"參數key。參數的值可以是"true"或者"approval"。至少有一個scope是approval才算是授權成功。(A grant is successful if at least one scope is approved.)

強制SSL

純HTTP對於測試來說是可以的,但是在生成中授權服務器應該使用SSL。你可以在一個安全的容器或代理后面運行應用程序,如果你正確地設置代理和容器(這與OAuth2無關),那么它應該可以正常工作。對於/authorize端點你需要把它當作正常的應用安全的一部分來做,對於/token端點在AuthorizationServerEndpointsConfigurer中有一個標記可以設置,通過用sslOnly()方法。

自定義錯誤處理

授權服務器用標准的Spring MVC特性來進行錯誤處理。

你可以提供自己的實現,通過添加@Controller並且帶上@RequestMapping("/oauth/error")

Mapping User Roles to Scopes

有時候,為了限制token的scope,不僅僅要根據指定的客戶端的范圍,也要根據用戶自己的權限來進行限制。如果你在你的AuthorizationEndpointDefaultOAuth2RequestFactory,你可以設置checkUserScopes=true來限制匹配的用戶角色的允許范圍。AuthorizationServerEndpointsConfigurer允許你注入一個自定義的OAuth2RequestFactory

資源服務器配置

一個資源服務器(可能與授權服務器是相同的應用,也可能與授權服務器是分開的應用)通過OAuth2 Token服務受保護的資源。

Spring OAuth 提供一個Spring Security認證過濾器來實現這個保護。你可以

你可以在一個@Configuration類上用@EnableResourceServer來切換它,並且用ResourceServerConfigurer配置它。下列特性可以被配置:

  • tokenServices  :一個ResourceServerTokenServices的實例
  • resourceId  :資源ID(推薦的,如果存在的話會被授權服務器校驗)
  • 資源服務器的其它擴展端點
  • request matchers for protected resources (defaults to all)
  • access rules for protected resources (defaults to plain "authenticated")
  • 其它通過HttpSecurity配置的自定義的受保護的資源

@EnableResourceServer注釋將自動添加一個OAuth2AuthenticationProcessingFilter類型的過濾器到Spring安全過濾器鏈中。

OAuth 2.0 客戶端

受保護的資源配置


 

受保護的資源(或者叫遠程資源)可以用OAuth2ProtectedResourceDetails類型的bean來定義。一個被保護的資源由下列屬性:

  • id :資源的id。這個id只是用於客戶端查找資源。
  • clientId :OAuth Client id。
  • clientSecret :關聯的資源的secret。默認非空
  • accessTokenUri :提供access_token的端點的uri
  • scope :逗號分隔的字符串,代表訪問資源的范圍。默認為空
  • clientAuthenticationScheme :客戶端認證所采用的schema。建議的值:"http_basic"和"form"。默認是"http_basic"。

不同的授權類型有不同的OAuth2ProtectedResourceDetails的具體實現(例如:ClientCredentialsResourceDetails是"client_credentials"類型的具體實現)

  • userAuthorizationUri :用戶授權uri,非必需的。

客戶端配置


 

對於OAuth 2.0客戶端配置,簡化的配置用@EnableOAuth2Client。這個注解做兩件事情:

  • 創建一個過濾器(ID是oauth2ClientContextFilter)來存儲當前的請求和上下文。在請求期間需要進行身份認證時,它管理重定向URI。
  • 在請求范圍內創建一個AccessTokenRequest類型的bean。對於授權代碼(或隱式)授予客戶端是很有用的,可以避免與單個用戶相關的狀態發生沖突。

AccessTokenRequest可以用在一個OAuth2RestTemplate,就像下面這樣:

@Autowired
private OAuth2ClientContext oauth2Context;

@Bean
public OAuth2RestTemplate sparklrRestTemplate() {
    return new OAuth2RestTemplate(sparklr(), oauth2Context);
}

訪問受保護的資源


 

建議用RestTemplate訪問受保護的資源。

Spring Security為OAuth提供了一個擴展的RestTemplate只需要你提供一個OAuth2ProtectedResourceDetails的實例即可。為了使它和用戶token(授權碼方式授權)一起使用,你應該考慮用@EnableOAuth2Client配置。

一般來說,web應用程序不應該使用密碼授予,因此如果您可以支持AuthorizationCodeResourceDetails,請避免使用ResourceOwnerPasswordResourceDetails

為了和用戶令牌(授權碼)一起使用,你應該考慮用@EnableOAuth2Client配置。

客戶端持久化Token


 

客戶端不需要持久化令牌,但是最好不要在每次重啟客戶端應用程序時都要求用戶批准新的令牌授予。

ClientTokenServices接口定義了為特定用戶保存OAuth 2.0令牌所需的操作。這是一個JDBC實現,但是如果您希望實現自己的服務,以便在持久數據庫中存儲訪問令牌和相關的身份驗證實例,則可以這樣做。如果你想要使用這個特性,你需要為OAuth2RestTemplate提供一個經過特殊配置的TokenProvider。例如:

@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public OAuth2RestOperations restTemplate() {
    OAuth2RestTemplate template = new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(accessTokenRequest));
    AccessTokenProviderChain provider = new AccessTokenProviderChain(Arrays.asList(new AuthorizationCodeAccessTokenProvider()));
    provider.setClientTokenServices(clientTokenServices());
    return template;
}

 

參考

https://projects.spring.io/spring-security-oauth/docs/oauth2.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM