Spring-security-OAuth 2開發指南

官方原文:http://projects.spring.io/spring-security-oauth/docs/oauth2.html

本文記錄官方文檔的重點內容,先記錄下來以供學習,裏面涉及到一些概念性的東西還需研究具體含義。


該指南共分爲兩部分:

OAuth 2.0 provider----------樣例代碼integration tests

OAuth 2.0 client-------------樣例代碼sample apps


一、OAuth 2.0 Provider

provider向用戶提供接口用於確認是否具有受保護資源的訪問權限

client用來獨立訪問或者代表用戶訪問受保護的資源


OAuth 2.0 Provider 實現


Spring OAuth2.0提供者分爲:

  • 授權服務 Authorization Service.

  • 資源服務 Resource Service.

with Spring Security OAuth you have the option to split them across two applications, and also to have multiple Resource Services that share an Authorization Service. 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. The following endpoints are required in the Spring Security filter chain in order to implement OAuth 2.0 Authorization Server:

它們有時會同時存在於一個應用程序中,但在Spring Security OAuth裏你可以分開部署,甚至可以擁有多個資源服務共享一個授權服務。所有獲取令牌的請求都將會在Spring MVC controller endpoints中進行處理,並且訪問受保護

的資源服務的處理流程將會放在標準的Spring Security請求過濾器中(filters)。

下面是配置一個授權服務要實現的endpoints:

  • AuthorizationEndpoint:用來作爲請求者獲得授權的服務,默認的URL是/oauth/authorize.

  • TokenEndpoint:用來作爲請求者獲得令牌(Token)的服務,默認的URL是/oauth/token.


下面是配置一個資源服務要實現的過濾器:

  • OAuth2AuthenticationProcessingFilter:用來作爲認證令牌(Token)的一個處理流程過濾器。只有當過濾器通過之後,請求者才能獲得受保護的資源。



1.Authorization Server Configuration授權服務配置

要配置授權服務,首先要考慮grant type授權類型(authorization code, user credentials, refresh token),不同的授權類型爲客戶端(Client)提供了不同的獲取令牌(Token)方式


The @EnableAuthorizationServer annotation is used to configure the OAuth 2.0 Authorization Server mechanism, together with any @Beans that implement AuthorizationServerConfigurer (there is a handy adapter implementation with empty methods). The following features are delegated to separate configurers that are created by Spring and passed into the AuthorizationServerConfigurer:

  • ClientDetailsServiceConfigurer: a configurer that defines the client details service. Client details can be initialized, or you can just refer to an existing store.

  • AuthorizationServerSecurityConfigurer: defines the security constraints on the token endpoint.

  • AuthorizationServerEndpointsConfigurer: defines the authorization and token endpoints and the token services.

可以使用@EnableAuthorizationServer註解來配置OAuth2.0的認證服務機制。同時使用實現了AuthorizationServerConfigurer@Beans註解的類。幾個配置是由Spring創建的獨立的配置對象,它們會被Spring傳入AuthorizationServerConfigurer中:

  • ClientDetailsServiceConfigurer: 用來配置客戶端詳情服務(ClientDetailsService),客戶端詳情信息在這裏進行初始化,你能夠把客戶端詳情信息通過指定的數據庫來存儲。

  • AuthorizationServerSecurityConfigurer: 用來配置令牌端點(Token Endpoint)的安全約束.

  • AuthorizationServerEndpointsConfigurer: 用來配置授權(authorization)以及令牌(token)的訪問端點和令牌服務(token services)。

Configuring 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. Important attributes of a client are

  • clientId: (required) the client id.

  • secret: (required for trusted clients) the client secret, if any.

  • scope: The scope to which the client is limited. If scope is undefined or empty (the default) the client is not limited by scope.

  • authorizedGrantTypes: Grant types that are authorized for the client to use. Default value is empty.

  • authorities: Authorities that are granted to the client (regular Spring Security authorities).

Client details can be updated in a running application by access the underlying store directly (e.g. database tables in the case of JdbcClientDetailsService) or through the ClientDetailsManager interface (which both implementations of ClientDetailsService also implement).

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.

ClientDetailsServiceConfigurer 能夠使用內存或者JDBC來實現客戶端詳情服務(ClientDetailsService),一個客戶端的重要的屬性有:

  • clientId:(必須的)用來標識客戶的Id。

  • secret:(需要值得信任的客戶端)客戶端安全碼,如果有的話。

  • scope:用來限制客戶端的訪問範圍,如果爲空(默認)的話,那麼客戶端擁有全部的訪問範圍。

  • authorizedGrantTypes:此客戶端可以使用的授權類型,默認爲空。

  • authorities:客戶端被授權的權限

 

Managing Tokens管理令牌

AuthorizationServerTokenServices 接口定義了一些管理OAuth 2.0 tokens的操作

  • When an  is created, the authentication must be stored so that resources accepting the access token can reference it later.訪問令牌(access token)被創建後一定要保存下來,這樣當一個客戶端使用這個令牌對資源服務進行請求的時候才能夠引用這個令牌

  • The access token is used to load the authentication that was used to authorize its creation.

當你創建自己的 AuthorizationServerTokenServices 實現時, 可以考慮一下使用DefaultTokenServices ,它有很多種策略可以改變access token的存儲方式. 使用它創建令牌時,默認的是一個隨機值,除了令牌的持久化是由TokenStore 實現的,其他的都是由DefaultTokenServices 實現的。 token默認時存儲在內存的,下面也有其他幾種方式:

  •  InMemoryTokenStore 默認的實現方式

  • The JdbcTokenStore 存儲在關係型數據庫中. 使用它時需要將 "spring-jdbc" 依賴放進你的classpath中

  • The JSON Web Token (JWT) version of the store encodes all the data about the grant into the token itself (so no back end store at all which is a significant advantage). One disadvantage is that you can't easily revoke an access token, so they normally are granted with short expiry and the revocation is handled at the refresh token. Another disadvantage is that the tokens can get quite large if you are storing a lot of user credential information in them. The JwtTokenStore is not really a "store" in the sense that it doesn't persist any data, but it plays the same role of translating betweeen token values and authentication information in the DefaultTokenServices.

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. Be sure to @EnableTransactionManagement to prevent clashes between client apps competing for the same rows when tokens are created. Note also that the sample schema has explicit PRIMARY KEY declarations - these are also necessary in a concurrent environment.

JWT Tokens

較少使用,暫時不寫


Grant Types授權類型

授權是使用 AuthorizationEndpoint 這個端點來進行控制的,通過 AuthorizationServerEndpointsConfigurer 來進行配置,默認情況下,除了password授權類型以外,支持所有授權類型,下面這些配置會影響到授權類型:

  • authenticationManager: 認證管理器,當你選擇了資源所有者密碼(password)授權類型的時候,請設置這個屬性注入一個 AuthenticationManager 對象。

  • userDetailsService: 如果你注入了一個 UserDetailsService 類(即你有了自己的UserDetailsService接口的實現)或者使用GlobalAuthenticationManagerConfigurert作了全局配置,那麼刷新令牌的授權就會檢查用戶的詳細信息user detail,以確保賬戶的有效性、

  • authorizationCodeServices:這個屬性是用來設置授權碼服務的,主要用於 "authorization_code" 授權碼類型模式。

  • implicitGrantService: 用來管理隱式授權模式.

  • tokenGranter: the TokenGranter 會掌控全部的授權控制,而忽略掉上面的屬性配置



Configuring the Endpoint URLs配置授權端點的url

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

  • The default (framework implementation) URL path for the endpoint 默認url

  • The custom path required (starting with a "/") 自定義的url,需要以"/"開頭

框架提供的url有:

 /oauth/authorize (the authorization endpoint), 

/oauth/token (the token endpoint), 

/oauth/confirm_access (user posts approval for grants here),用戶確認授權提交端點

/oauth/error (used to render errors in the authorization server), 授權服務錯誤信息端點

/oauth/check_token (used by Resource Servers to decode access tokens)資源服務訪問令牌解析端點

/oauth/token_key (exposes public key for token verification if using JWT tokens).使用JWT令牌時,提供令牌認證的公有密鑰的端點

N.B. the Authorization endpoint /oauth/authorize (或者它的映射端點)應該被Spring Security保護起來只供授權用戶訪問. 例如,使用標準的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
    }

Note: 如果你的服務既是Authorization Server 又是 Resource Server ,那麼是一個低優先級的過濾器鏈來控制API資源的. 這些接口被access tokens所保護, you need their paths not to be matched by the ones in the main user-facing filter chain, so be sure to include a request matcher that picks out only non-API resources in the WebSecurityConfigurer above.

所以請挑選一個URL鏈接來確保你的資源接口中有一個不需要被保護的鏈接用來取得授權,就如上面示例中的 /login 鏈接,你需要在 WebSecurityConfigurer 配置對象中進行設置。

The token endpoint is protected for you by default by Spring OAuth in the @Configuration support using HTTP Basic authentication of the client secret. This is not the case in XML (so it should be protected explicitly).

In XML the <authorization-server/> element has some attributes that can be used to change the default endpoint URLs in a similar way. The /check_token endpoint has to be explicitly enabled (with the check-token-enabled attribute).

Customizing the UI

。。。

Enforcing SSL

使用普通的http請求來測試是可以的,但是在生產環境中Authorization Server 必須使用SSL. 你可以在一個安全的容器中運行app或者使用代理,你也可以使用Spring Security的requiresChannel()約束來保證端點的安全性. 對於 /authorize 端點來說,你可以自由決定使用哪種方法來讓/authorize端口作爲你正常應用安全的一部分,即要保證這個端口是安全的. 對於 /token 端點來說, 在 AuthorizationServerEndpointsConfigurer 中有一個標記,可以使用 sslOnly() 方法來設置. In both cases the secure channel setting is optional but will cause Spring Security to redirect to what it thinks is a secure channel if it detects a request on an insecure channel.

Customizing the Error Handling

Error handling in an Authorization Server uses standard Spring MVC features, namely @ExceptionHandler methods in the endpoints themselves. Users can also provide a WebResponseExceptionTranslator to the endpoints themselves which is the best way to change the content of the responses as opposed to the way they are rendered. The rendering of exceptions delegates to HttpMesssageConverters (which can be added to the MVC configuration) in the case of token endpoint and to the OAuth error view (/oauth/error) in the case of teh authorization endpoint. The whitelabel error endpoint is provided for HTML responses, but users probably need to provide a custom implementation (e.g. just add a @Controllerwith @RequestMapping("/oauth/error")).端點實際上就是一個特殊的Controller,它用於返回一些對象數據。

2.Resource Server Configuration資源服務配置

資源服務器爲受OAuth2令牌保護的資源提供服務. 由Spring OAuth提供的一個Spring Security認證過濾器來實現. 在一個 @Configuration 類上加上 @EnableResourceServer 註解, 必要的時候是 ResourceServerConfigurer.來作進一步的配置。可配置內容如下:

  • tokenServices: ResourceServerTokenServices)實例,用來實現token服務

  • resourceId: 資源id (optional, but recommended and will be validated by the auth server if present).

  • 其他的擴展屬性(例如 tokenExtractor 令牌提取器,從進來的請求中提取令牌

  • request matchers for protected resources (defaults to all)請求匹配器,用來設置需要進行保護的資源路徑,默認的情況下是受保護資源服務的全部路徑

  • access rules for protected resources (defaults to plain "authenticated")受保護資源的訪問規則,默認的規則是普通的身份驗證

  • other customizations for the protected resources permitted by the HttpSecurity configurer in Spring Security其他的自定義權限保護規則通過 HttpSecurity 來進行配置

加了 @EnableResourceServer 註解會自動 向Spring Security 的過濾器鏈添加一個類型爲 OAuth2AuthenticationProcessingFilter 的過濾器

In XML there is a <resource-server/> element with an id attribute - this is the bean id for a servlet Filter that can then be added manually to the standard Spring Security chain.

Your ResourceServerTokenServices 是組成Authorization Server的另一半. If the Resource Server and Authorization Server are in the same application and you use DefaultTokenServices then you don't have to think too hard about this because it implements all the necessary interfaces so it is automatically consistent. If your Resource Server is a separate application then you have to make sure you match the capabilities of the Authorization Server and provide a ResourceServerTokenServices that knows how to decode the tokens correctly. As with the Authorization Server, you can often use the DefaultTokenServices and the choices are mostly expressed through the TokenStore (backend storage or local encoding). An alternative is the RemoteTokenServices which is a Spring OAuth features (not part of the spec) allowing Resource Servers to decode tokens through an HTTP resource on the Authorization Server (/oauth/check_token). RemoteTokenServices are convenient if there is not a huge volume of traffic in the Resource Servers (every request has to be verified with the Authorization Server), or if you can afford to cache the results. To use the /oauth/check_token endpoint you need to expose it by changing its access rule (default is "denyAll()") in the AuthorizationServerSecurityConfigurer, e.g.

		@Override
		public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
			oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
					"hasAuthority('ROLE_TRUSTED_CLIENT')");
		}

In this example we are configuring both the /oauth/check_token endpoint and the /oauth/token_key endpoint (so trusted resources can obtain the public key for JWT verification). These two endpoints are protected by HTTP Basic authentication using client credentials.

Configuring An OAuth-Aware Expression Handler

You may want to take advantage of Spring Security's expression-based access control. An expression handler will be registered by default in the @EnableResourceServer setup. The expressions include #oauth2.clientHasRole#oauth2.clientHasAnyRole, and #oath2.denyClient which can be used to provide access based on the role of the oauth client (see OAuth2SecurityExpressionMethods for a comprehensive list). In XML you can register a oauth-aware expression handler with the expression-handler element of the regular <http/> security configuration.

二、OAuth 2.0 Client

The OAuth 2.0 client 機制是 負責 訪問受OAuth 2.0保護的其他服務的資源. 配置包含建立用戶可以訪問的相關受保護資源、存儲用戶的認證碼和訪問令牌

Protected Resource Configuration受保護的資源配置

Protected resources (or "remote resources") 由 OAuth2ProtectedResourceDetails.的實現類來實現。 一個受保護的資源由如下屬性:

  • id: 資源id. 客戶端用它來查找資源; 

  • clientId: OAuth客戶端ID. OAuth provider用它來識別你的客戶端

  • clientSecret: 與資源有關的密鑰,默認爲空.

  • accessTokenUri: 提供訪問令牌的OAuth提供者端點的uri

  • scope: Comma-separted list of strings specifying the scope of the access to the resource. By default, no scope will be specified.

  • clientAuthenticationScheme: The scheme used by your client to authenticate to the access token endpoint. Suggested values: "http_basic" and "form". Default: "http_basic". See section 2.1 of the OAuth 2 spec.

Different grant types have different concrete implementations of OAuth2ProtectedResourceDetails (e.g. ClientCredentialsResource for "client_credentials" grant type). For grant types that require user authorization there is a further property:

  • userAuthorizationUri: The uri to which the user will be redirected if the user is ever needed to authorize access to the resource. Note that this is not always required, depending on which OAuth 2 profiles are supported.

In XML there is a <resource/> element that can be used to create a bean of type OAuth2ProtectedResourceDetails. It has attributes matching all the properties above.

Client Configuration客戶端配置

很簡單,使用 @EnableOAuth2Client就行。這個配置會做兩件事:

  • 創建一個過濾器bean (with ID oauth2ClientContextFilter) 存儲當前請求和上下文. 如果在一個請求中需要認證,它管理了OAuth authentication uri的重定向的源地址和目的地址

  • 在請求範圍內創建一個類型爲 AccessTokenRequest 的bean. 授權類型是認證碼授權或者隱式授權時,客戶端能避免個體之間的衝突.

這個過濾器必須裝入到應用程序中(e.g. using a Servlet initializer or web.xml configuration for a DelegatingFilterProxy with the same name).

The AccessTokenRequest can be used in an OAuth2RestTemplate like this:

@Autowired
private OAuth2ClientContext oauth2Context;

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

The OAuth2ClientContext is placed (for you) in session scope to keep the state for different users separate. Without that you would have to manage the equivalent data structure yourself on the server, mapping incoming requests to users, and associating each user with a separate instance of the OAuth2ClientContext.

In XML there is a <client/> element with an id attribute - this is the bean id for a servlet Filter that must be mapped as in the @Configurationcase to a DelegatingFilterProxy (with the same name).

Accessing Protected Resources訪問受保護的資源

一旦你提供好了所有的資源配置,你就可以訪問那些資源了. 推薦的方法是使用spring3中的 RestTemplate . OAuth for Spring Security 提供了一個RestTemplate 的擴展,只需要提供一個 OAuth2ProtectedResourceDetails.實例。 To use it with user-tokens (authorization code grants) you should consider using the @EnableOAuth2Client configuration (or the XML equivalent <oauth:rest-template/>) which creates some request and session scoped context objects so that requests for different users do not collide at runtime.

As a general rule, a web application should not use password grants, so avoid using ResourceOwnerPasswordResourceDetails if you can in favour of AuthorizationCodeResourceDetails. If you desparately need password grants to work from a Java client, then use the same mechanism to configure your OAuth2RestTemplate and add the credentials to the AccessTokenRequest (which is a Map and is ephemeral) not the ResourceOwnerPasswordResourceDetails (which is shared between all access tokens).

Persisting Tokens in a Client持久化令牌

A client does not need to persist tokens, but it can be nice for users to not be required to approve a new token grant every time the client app is restarted. The ClientTokenServices interface defines the operations that are necessary to persist OAuth 2.0 tokens for specific users. There is a JDBC implementation provided, but you can if you prefer implement your own service for storing the access tokens and associated authentication instances in a persistent database. If you want to use this feature you need provide a specially configured TokenProvider to the OAuth2RestTemplatee.g.

@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;}

Customizations for Clients of External OAuth2 Providers

Some external OAuth2 providers (e.g. Facebook) do not quite implement the specification correctly, or else they are just stuck on an older version of the spec than Spring Security OAuth. To use those providers in your client application you might need to adapt various parts of the client-side infrastructure.

To use Facebook as an example, there is a Facebook feature in the tonr2 application (you need to change the configuration to add your own, valid, client id and secret - they are easy to generate on the Facebook website).

Facebook token responses also contain a non-compliant JSON entry for the expiry time of the token (they use expires instead of expires_in), so if you want to use the expiry time in your application you will have to decode it manually using a custom OAuth2SerializationService.




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章