Spring Security OAuth 2.0

·前一篇《OAuth 2.0html

OAuth 2.0 Provider 實現

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

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

(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. )github

爲了實現OAuth 2.0受權服務器,在Spring Security的過濾器鏈中須要有如下端點:web

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

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

  • OAuth2AuthenticationProcessingFilter    用於加載認證

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

Authorization Server 配置

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

服務器的配置是用來提供client detail服務和token服務的,而且能夠啓用或者禁用全局的某些機制。後端

每一個客戶端能夠配置不一樣的權限api

@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

相關文章
相關標籤/搜索