Spring Security OAuth2 開發指南(非最新版本)


Spring OAuth2.0 提供者實現原理:

Spring OAuth2.0提供者實際上分為:
  • 授權服務 Authorization Service.
  • 資源服務 Resource Service.
雖然這兩個提供者有時候可能存在同一個應用程序中,但在Spring Security OAuth中你可以把
他它們各自放在不同的應用上,而且你可以有多個資源服務,它們共享同一個中央授權服
務。
 
所有獲取令牌的請求都將會在Spring MVC controller endpoints中進行處理,並且訪問受保護
的資源服務的處理流程將會放在標准的Spring Security請求過濾器中(filters)。
 
下面是配置一個授權服務必須要實現的endpoints:
  • AuthorizationEndpoint:用來作為請求者獲得授權的服務,默認的URL是/oauth/authorize.
  • TokenEndpoint:用來作為請求者獲得令牌(Token)的服務,默認的URL是/oauth/token.
 
下面是配置一個資源服務必須要實現的過濾器:
  • OAuth2AuthenticationProcessingFilter:用來作為認證令牌(Token)的一個處理流程過濾器。只有當過濾器通過之后,請求者才能獲得受保護的資源。
 
配置提供者(授權、資源)都可以通過簡單的Java注解@Configuration來進行適配,你也可以使用基於XML的聲明式語法來進行配置,如果你打算這樣做的話,那么請使用http://www.springframework.org/schema/security/spring-security-oauth2.xsd來作為XML的schema(即XML概要定義)以及使用http://www.springframework.org/schema/security/oauth2來作為命名空間。
 
 
一、授權服務配置:

配置一個授權服務,你需要考慮幾種授權類型(Grant Type),不同的授權類型為客戶端(Client)提供了不同的獲取令牌(Token)方式,為了實現並確定這幾種授權,需要配置使用 ClientDetailsService 和 TokenService 來開啟或者禁用這幾種授權機制。到這里就請注意了,不管你使用什么樣的授權類型(Grant Type),每一個客戶端(Client)都能夠通過明確的配置以及權限來實現不同的授權訪問機制。這也就是說,假如你提供了一個支持"client_credentials"的授權方式,並不意味着客戶端就需要使用這種方式來獲得授權。下面是幾種授權類型的列表,具體授權機制的含義可以參見RFC6749( 中文版本):
  • authorization_code:授權碼類型。
  • implicit:隱式授權類型。
  • password:資源所有者(即用戶)密碼類型。
  • client_credentials:客戶端憑據(客戶端ID以及Key)類型。
  • refresh_token:通過以上授權獲得的刷新令牌來獲取新的令牌。
 
可以用 @EnableAuthorizationServer 注解來配置OAuth2.0 授權服務機制,通過使用@Bean注解的幾個方法一起來配置這個授權服務。下面咱們介紹幾個配置類,這幾個配置是由Spring創建的獨立的配置對象,它們會被Spring傳入AuthorizationServerConfigurer中:
  • ClientDetailsServiceConfigurer:用來配置客戶端詳情服務(ClientDetailsService),客戶端詳情信息在這里進行初始化,你能夠把客戶端詳情信息寫死在這里或者是通過數據庫來存儲調取詳情信息。
  • AuthorizationServerSecurityConfigurer:用來配置令牌端點(Token Endpoint)的安全約束.
  • AuthorizationServerEndpointsConfigurer:用來配置授權(authorization)以及令牌(token)的訪問端點和令牌服務(token services)。
(譯者注:以上的配置可以選擇繼承AuthorizationServerConfigurerAdapter並且覆寫其中的三個configure方法來進行配置。)
 
配置授權服務一個比較重要的方面就是提供一個授權碼給一個OAuth客戶端(通過 authorization_code 授權類型),一個授權碼的獲取是OAuth客戶端跳轉到一個授權頁面,然后通過驗證授權之后服務器重定向到OAuth客戶端,並且在重定向連接中附帶返回一個授權碼。
 
如果你是通過XML來進行配置的話,那么可以使用 <authorization-server/> 標簽來進行配置。
 
(譯者注:想想現在國內各大平台的社會化登陸服務,例如騰訊,用戶要使用QQ登錄到某個網站,這個網站是跳轉到了騰訊的登陸授權頁面,然后用戶登錄並且確定授權之后跳轉回目標網站,這種授權方式規范在我上面提供的鏈接*RFC6749*的第4.1節有詳細闡述。)
 
配置客戶端詳情信息(Client Details):
ClientDetailsServiceConfigurer (AuthorizationServerConfigurer 的一個回調配置項,見上的概述) 能夠使用內存或者JDBC來實現客戶端詳情服務(ClientDetailsService),有幾個重要的屬性如下列表:
  • clientId:(必須的)用來標識客戶的Id。
  • secret:(需要值得信任的客戶端)客戶端安全碼,如果有的話。
  • scope:用來限制客戶端的訪問范圍,如果為空(默認)的話,那么客戶端擁有全部的訪問范圍。
  • authorizedGrantTypes:此客戶端可以使用的授權類型,默認為空。
  • authorities:此客戶端可以使用的權限(基於Spring Security authorities)。
 
客戶端詳情(Client Details)能夠在應用程序運行的時候進行更新,可以通過訪問底層的存儲服務(例如將客戶端詳情存儲在一個關系數據庫的表中,就可以使用 JdbcClientDetailsService)或者通過 ClientDetailsManager 接口(同時你也可以實現 ClientDetailsService 接口)來進行管理。
(譯者注:不過我並沒有找到 ClientDetailsManager 這個接口文件,只找到了 ClientDetailsService)
 
管理令牌(Managing Token):
AuthorizationServerTokenServices 接口定義了一些操作使得你可以對令牌進行一些必要的管理,在使用這些操作的時候請注意以下幾點:
  • 當一個令牌被創建了,你必須對其進行保存,這樣當一個客戶端使用這個令牌對資源服務進行請求的時候才能夠引用這個令牌。
  • 當一個令牌是有效的時候,它可以被用來加載身份信息,里面包含了這個令牌的相關權限。
 
當你自己創建 AuthorizationServerTokenServices 這個接口的實現時,你可能需要考慮一下使用 DefaultTokenServices 這個類,里面包含了一些有用實現,你可以使用它來修改令牌的格式和令牌的存儲。默認的,當它嘗試創建一個令牌的時候,是使用隨機值來進行填充的,除了持久化令牌是委托一個 TokenStore 接口來實現以外,這個類幾乎幫你做了所有的事情。並且 TokenStore 這個接口有一個默認的實現,它就是 InMemoryTokenStore ,如其命名,所有的令牌是被保存在了內存中。除了使用這個類以外,你還可以使用一些其他的預定義實現,下面有幾個版本,它們都實現了TokenStore接口:
  • InMemoryTokenStore:這個版本的實現是被默認采用的,它可以完美的工作在單服務器上(即訪問並發量壓力不大的情況下,並且它在失敗的時候不會進行備份),大多數的項目都可以使用這個版本的實現來進行嘗試,你可以在開發的時候使用它來進行管理,因為不會被保存到磁盤中,所以更易於調試。
  • JdbcTokenStore:這是一個基於JDBC的實現版本,令牌會被保存進關系型數據庫。使用這個版本的實現時,你可以在不同的服務器之間共享令牌信息,使用這個版本的時候請注意把"spring-jdbc"這個依賴加入到你的classpath當中。
  • JwtTokenStore:這個版本的全稱是 JSON Web Token(JWT),它可以把令牌相關的數據進行編碼(因此對於后端服務來說,它不需要進行存儲,這將是一個重大優勢),但是它有一個缺點,那就是撤銷一個已經授權令牌將會非常困難,所以它通常用來處理一個生命周期較短的令牌以及撤銷刷新令牌(refresh_token)。另外一個缺點就是這個令牌占用的空間會比較大,如果你加入了比較多用戶憑證信息。JwtTokenStore 不會保存任何數據,但是它在轉換令牌值以及授權信息方面與 DefaultTokenServices 所扮演的角色是一樣的。
 
 
JWT令牌(JWT Tokens):
使用JWT令牌你需要在授權服務中使用 JwtTokenStore,資源服務器也需要一個解碼的Token令牌的類 JwtAccessTokenConverter,JwtTokenStore依賴這個類來進行編碼以及解碼,因此你的授權服務以及資源服務都需要使用這個轉換類。Token令牌默認是有簽名的,並且資源服務需要驗證這個簽名,因此呢,你需要使用一個對稱的Key值,用來參與簽名計算,這個Key值存在於授權服務以及資源服務之中。或者你可以使用非對稱加密算法來對Token進行簽名,Public Key公布在/oauth/token_key這個URL連接中,默認的訪問安全規則是"denyAll()",即在默認的情況下它是關閉的,你可以注入一個標准的 SpEL 表達式到 AuthorizationServerSecurityConfigurer 這個配置中來將它開啟(例如使用"permitAll()"來開啟可能比較合適,因為它是一個公共密鑰)。
 
如果你要使用 JwtTokenStore,請務必把"spring-security-jwt"這個依賴加入到你的classpath中。
 
 
配置授權類型(Grant Types):
授權是使用 AuthorizationEndpoint 這個端點來進行控制的,你能夠使用 AuthorizationServerEndpointsConfigurer 這個對象的實例來進行配置(AuthorizationServerConfigurer 的一個回調配置項,見上的概述) ,如果你不進行設置的話,默認是除了資源所有者密碼(password)授權類型以外,支持其余所有標准授權類型的(RFC6749),我們來看一下這個配置對象有哪些屬性可以設置吧,如下列表:
  • authenticationManager:認證管理器,當你選擇了資源所有者密碼(password)授權類型的時候,請設置這個屬性注入一個 AuthenticationManager 對象。
  • userDetailsService:如果啊,你設置了這個屬性的話,那說明你有一個自己的 UserDetailsService 接口的實現,或者你可以把這個東西設置到全局域上面去(例如 GlobalAuthenticationManagerConfigurer 這個配置對象),當你設置了這個之后,那么 "refresh_token" 即刷新令牌授權類型模式的流程中就會包含一個檢查,用來確保這個賬號是否仍然有效,假如說你禁用了這個賬戶的話。
  • authorizationCodeServices:這個屬性是用來設置授權碼服務的(即 AuthorizationCodeServices 的實例對象),主要用於 "authorization_code" 授權碼類型模式。
  • implicitGrantService:這個屬性用於設置隱式授權模式,用來管理隱式授權模式的狀態。
  • tokenGranter:這個屬性就很牛B了,當你設置了這個東西(即 TokenGranter 接口實現),那么授權將會交由你來完全掌控,並且會忽略掉上面的這幾個屬性,這個屬性一般是用作拓展用途的,即標准的四種授權模式已經滿足不了你的需求的時候,才會考慮使用這個。
 
在XML配置中呢,你可以使用 "authorization-server" 這個標簽元素來進行設置。
 
 
配置授權端點的URL(Endpoint URLs):
AuthorizationServerEndpointsConfigurer 這個配置對象(AuthorizationServerConfigurer 的一個回調配置項,見上的概述) 有一個叫做 pathMapping() 的方法用來配置端點URL鏈接,它有兩個參數:
  • 第一個參數:String 類型的,這個端點URL的默認鏈接。
  • 第二個參數:String 類型的,你要進行替代的URL鏈接。
以上的參數都將以 "/" 字符為開始的字符串,框架的默認URL鏈接如下列表,可以作為這個 pathMapping() 方法的第一個參數:
  • /oauth/authorize:授權端點。
  • /oauth/token:令牌端點。
  • /oauth/confirm_access:用戶確認授權提交端點。
  • /oauth/error:授權服務錯誤信息端點。
  • /oauth/check_token:用於資源服務訪問的令牌解析端點。
  • /oauth/token_key:提供公有密匙的端點,如果你使用JWT令牌的話。
 
需要注意的是授權端點這個URL應該被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
}
復制代碼
注意:如果你的應用程序中既包含授權服務又包含資源服務的話,那么這里實際上是另一個的低優先級的過濾器來控制資源接口的,這些接口是被保護在了一個訪問令牌(access token)中,所以請挑選一個URL鏈接來確保你的資源接口中有一個不需要被保護的鏈接用來取得授權,就如上面示例中的 /login 鏈接,你需要在 WebSecurityConfigurer 配置對象中進行設置。
 
令牌端點默認也是受保護的,不過這里使用的是基於 HTTP Basic Authentication 標准的驗證方式來驗證客戶端的,這在XML配置中是無法進行設置的(所以它應該被明確的保護)。
 
在XML配置中可以使用 <authorization-server/> 元素標簽來改變默認的端點URLs,注意在配置 /check_token 這個鏈接端點的時候,使用 check-token-enabled 屬性標記啟用。
 
 
強制使用SSL(Enforcing SSL):
使用簡單的HTTP請求來進行測試是可以的,但是如果你要部署到產品環境上的時候,你應該永遠都使用SSL來保護授權服務器在與客戶端進行通訊的時候進行加密。你可以把授權服務應用程序放到一個安全的運行容器中,或者你可以使用一個代理,如果你設置正確了的話它們應該工作的很好(這樣的話你就不需要設置任何東西了)。
但是也許你可能希望使用 Spring Security 的 requiresChannel() 約束來保證安全,對於授權端點來說(還記得上面的列表嗎,就是那個 /authorize 端點),它應該成為應用程序安全連接的一部分,而對於 /token 令牌端點來說的話,它應該有一個標記被配置在 AuthorizationServerEndpointsConfigurer 配置對象中,你可以使用 sslOnly() 方法來進行設置。當然了,這兩個設置是可選的,不過在以上兩種情況中,會導致Spring Security 會把不安全的請求通道重定向到一個安全通道中。(譯者注:即將HTTP請求重定向到HTTPS請求上)。
 
 
自定義錯誤處理(Error Handling):
端點實際上就是一個特殊的Controller,它用於返回一些對象數據。
授權服務的錯誤信息是使用標准的Spring MVC來進行處理的,也就是 @ExceptionHandler 注解的端點方法,你也可以提供一個 WebResponseExceptionTranslator 對象。最好的方式是改變響應的內容而不是直接進行渲染。
假如說在呈現令牌端點的時候發生了異常,那么異常委托了 HttpMessageConverters 對象(它能夠被添加到MVC配置中)來進行輸出。假如說在呈現授權端點的時候未通過驗證,則會被重定向到 /oauth/error 即錯誤信息端點中。whitelabel error (即Spring框架提供的一個默認錯誤頁面)錯誤端點提供了HTML的響應,但是你大概可能需要實現一個自定義錯誤頁面(例如只是簡單的增加一個 @Controller 映射到請求路徑上 @RequestMapping("/oauth/error"))。
 
 
映射用戶角色到權限范圍(Mapping User Roles to Scopes):
有時候限制令牌的權限范圍是很有用的,這不僅僅是針對於客戶端,你還可以根據用戶的權限來進行限制。如果你使用 DefaultOAuth2RequestFactory 來配置 AuthorizationEndpoint 的話你可以設置一個flag即 checkUserScopes=true來限制權限范圍,不過這只能匹配到用戶的角色。你也可以注入一個 OAuth2RequestFactory 到 TokenEnpoint 中,不過這只能工作在 password 授權模式下。如果你安裝一個 TokenEndpointAuthenticationFilter 的話,你只需要增加一個過濾器到 HTTP BasicAuthenticationFilter 后面即可。當然了,你也可以實現你自己的權限規則到 scopes 范圍的映射和安裝一個你自己版本的 OAuth2RequestFactory。AuthorizationServerEndpointConfigurer 配置對象允許你注入一個你自定義的 OAuth2RequestFactory,因此你可以使用這個特性來設置這個工廠對象,前提是你使用 @EnableAuthorizationServer 注解來進行配置(見上面介紹的授權服務配置)。
 
 
 
二、資源服務配置:

一個資源服務(可以和授權服務在同一個應用中,當然也可以分離開成為兩個不同的應用程序)提供一些受token令牌保護的資源,Spring OAuth提供者是通過Spring Security authentication filter 即驗證過濾器來實現的保護,你可以通過 @EnableResourceServer 注解到一個 @Configuration 配置類上,並且必須使用 ResourceServerConfigurer 這個配置對象來進行配置(可以選擇繼承自 ResourceServerConfigurerAdapter 然后覆寫其中的方法,參數就是這個對象的實例),下面是一些可以配置的屬性:
  • tokenServices:ResourceServerTokenServices 類的實例,用來實現令牌服務。
  • resourceId:這個資源服務的ID,這個屬性是可選的,但是推薦設置並在授權服務中進行驗證。
  • 其他的拓展屬性例如 tokenExtractor 令牌提取器用來提取請求中的令牌。
  • 請求匹配器,用來設置需要進行保護的資源路徑,默認的情況下是受保護資源服務的全部路徑。
  • 受保護資源的訪問規則,默認的規則是簡單的身份驗證(plain authenticated)。
  • 其他的自定義權限保護規則通過 HttpSecurity 來進行配置。
 
@EnableResourceServer 注解自動增加了一個類型為 OAuth2AuthenticationProcessingFilter 的過濾器鏈,
 
在XML配置中,使用 <resource-server />標簽元素並指定id為一個servlet過濾器就能夠手動增加一個標准的過濾器鏈。
 
ResourceServerTokenServices 是組成授權服務的另一半,如果你的授權服務和資源服務在同一個應用程序上的話,你可以使用 DefaultTokenServices ,這樣的話,你就不用考慮關於實現所有必要的接口的一致性問題,這通常是很困難的。如果你的資源服務器是分離開的,那么你就必須要確保能夠有匹配授權服務提供的 ResourceServerTokenServices,它知道如何對令牌進行解碼。
在授權服務器上,你通常可以使用 DefaultTokenServices 並且選擇一些主要的表達式通過 TokenStore(后端存儲或者本地編碼)。
RemoteTokenServices 可以作為一個替代,它將允許資源服務器通過HTTP請求來解碼令牌(也就是授權服務的 /oauth/check_token 端點)。如果你的資源服務沒有太大的訪問量的話,那么使用RemoteTokenServices 將會很方便(所有受保護的資源請求都將請求一次授權服務用以檢驗token值),或者你可以通過緩存來保存每一個token驗證的結果。
使用授權服務的 /oauth/check_token 端點你需要將這個端點暴露出去,以便資源服務可以進行訪問,這在咱們授權服務配置中已經提到了,下面是一個例子:
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
    oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')"); }

 

在這個例子中,我們配置了 /oauth/check_token 和 /oauth/token_key 這兩個端點(受信任的資源服務能夠獲取到公有密匙,這是為了驗證JWT令牌)。這兩個端點使用了HTTP Basic Authentication 即HTTP基本身份驗證,使用 client_credentials 授權模式可以做到這一點。
 
 
配置OAuth-Aware表達式處理器(OAuth-Aware Expression Handler):
你也許希望使用 Spring Security's expression-based access control 來獲得一些優勢,一個表達式處理器會被注冊到默認的 @EnableResourceServer 配置中,這個表達式包含了 #oauth2.clientHasRole,#oauth2.clientHasAnyRole 以及 #oauth2.denyClient 所提供的方法來幫助你使用權限角色相關的功能(在 OAuth2SecurityExpressionMethods 中有完整的列表)。
在XML配置中你可以注冊一個 OAuth-Aware 表達式處理器即 <expression-handler />元素標簽到 常規的 <http /> 安全配置上。
 
 


免責聲明!

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



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