SpringSecurityOAuth2開發者指南譯

NO IMAGE

介紹

這是用戶指南的支持OAuth 2.0。對於OAuth 1.0,一切都是不同的,所以看到它的用戶指南

本用戶指南分為兩部分,第一部分為OAuth 2.0提供者,第二部分為OAuth 2.0客戶端。對於提供商和客戶端,示例代碼的最佳來源是集成測試示例應用程序

OAuth 2.0提供程序

OAuth 2.0提供者機制負責公開OAuth 2.0受保護的資源。該配置包括建立可獨立或代表用戶訪問其受保護資源的OAuth 2.0客戶端。提供者通過管理和驗證用於訪問受保護資源的OAuth 2.0令牌來實現。在適用的情況下,提供商還必須提供用戶界面,以確認客戶端可以被授權訪問受保護資源(即確認頁面)。

OAuth 2.0提供程序實現

OAuth 2.0中的提供者角色實際上是在授權服務和資源服務之間分割的,而有時它們位於同一個應用程序中,使用Spring Security OAuth,您可以選擇在兩個應用程序之間進行拆分,並且還可以共享多個資源服務授權服務。令牌的請求由Spring MVC控制器端點處理,對受保護資源的訪問由標準的Spring Security請求過濾器處理。為了實現OAuth 2.0授權服務器,Spring Security過濾器鏈中需要以下端點:

實施OAuth 2.0資源服務器需要以下過濾器:

對於所有OAuth 2.0提供程序功能,使用特殊的Spring OAuth @Configuration適配器簡化了配置。還有一個用於OAuth配置的XML命名空間,並且模式位於www.springframework.org/schema/secu…。命名空間是http://www.springframework.org/schema/security/oauth2

授權服務器配置

在配置授權服務器時,必須考慮客戶端用於從最終用戶獲取訪問令牌(例如授權代碼,用戶憑據,刷新令牌)的授權類型。服務器的配置用於提供客戶端詳細信息服務和令牌服務的實現,並且啟用或禁用全局機制的某些方面。但是請注意,每個客戶端都可以特別配置,以便能夠使用某些授權機制和訪問授權。也就是因為您的提供商配置為支持“客戶端憑據”授權類型,並不意味著特定客戶端被授權使用該授權類型。

@EnableAuthorizationServer註釋用於配置OAuth 2.0授權服務器機制,以及任何@Beans實現AuthorizationServerConfigurer(有一個方便的適配器實現)。將以下功能委派給由Spring創建並傳遞到以下內容的單獨配置程序AuthorizationServerConfigurer

  • ClientDetailsServiceConfigurer:一個定義客戶端詳細信息服務的配置程序。客戶端的詳細信息可以初始化,也可以參考現有的存儲。
  • AuthorizationServerSecurityConfigurer:定義令牌端點上的安全約束。
  • AuthorizationServerEndpointsConfigurer:定義授權和令牌端點和令牌服務。

提供商配置的一個重要方面是授權代碼提供給OAuth客戶端(授權代碼授權)的方式。授權代碼由OAuth客戶端通過將最終用戶指向用戶可以輸入其憑據的授權頁面獲得,導致從提供商授權服務器重定向到具有授權碼的OAuth客戶端。這在OAuth 2規範中有詳細說明。

在XML中,有一個<authorization-server/>元素以類似的方式用於配置OAuth 2.0授權服務器。

配置客戶端詳細信息

ClientDetailsServiceConfigurer(從您的回調AuthorizationServerConfigurer)可以用來在內存或JDBC實現客戶的細節服務來定義的。客戶端的重要屬性是

  • clientId:(必填)客戶端ID。
  • secret:(可信客戶端需要)客戶機密碼(如果有)。
  • scope:客戶受限的範圍。如果範圍未定義或為空(默認值),客戶端不受範圍限制。
  • authorizedGrantTypes:授予客戶端使用授權的類型。默認值為空。
  • authorities授予客戶的授權機構(普通的Spring Security權威機構)。

客戶端的詳細信息可以通過直接訪問底層商店(例如,在數據庫表中JdbcClientDetailsService)或通過ClientDetailsManager接口(這兩種實現ClientDetailsService也實現)來更新運行的應用程序。

注意:JDBC服務的架構未與庫一起打包(因為在實踐中可能需要使用太多變體),而是可以從github中的測試代碼中開始。

管理令牌

AuthorizationServerTokenServices接口定義了所必需的管理OAuth 2.0令牌的操作。請注意以下事項:

  • 當創建訪問令牌時,必須存儲身份驗證,以便接受訪問令牌的資源可以稍後引用。
  • 訪問令牌用於加載用於授權其創建的認證。

在創建AuthorizationServerTokenServices實現時,您可能需要考慮使用DefaultTokenServices可插入的策略來更改訪問令牌的格式和存儲。默認情況下,它將通過隨機值創建令牌,並處理除代表它的令牌持久化之外的所有內容TokenStore。默認存儲是內存中的實現,但還有一些其他可用的實現。這是一個關於每一個的一些討論的描述

  • 默認值InMemoryTokenStore對於單個服務器是完全正常的(即,在發生故障的情況下,低流量和熱備份備份服務器)。大多數項目可以從這裡開始,也可以在開發模式下運行,以便輕鬆啟動沒有依賴關係的服務器。
  • JdbcTokenStore是同一件事的JDBC版本,它將令牌數據存儲在關係數據庫中。如果您可以在服務器之間共享數據庫,則可以使用JDBC版本,如果只有一個,則擴展同一服務器的實例,或者如果有多個組件,則授權和資源服務器。要使用JdbcTokenStore你需要“spring-jdbc”的類路徑。
  • 商店的JSON Web令牌(JWT)版本將所有關於授權的數據編碼到令牌本身(因此,根本沒有後端存儲是一個顯著的優勢)。一個缺點是您不能輕易地撤銷訪問令牌,因此通常被授予短期到期權,撤銷在刷新令牌處理。另一個缺點是,如果您在其中存儲了大量用戶憑據信息,令牌可能會變得非常大。這JwtTokenStore不是一個真正的“商店”,因為它不會保留任何數據,但它在翻譯令牌值和驗證信息之間起著相同的作用DefaultTokenServices

注意:JDBC服務的架構未與庫一起打包(因為在實踐中可能需要使用太多變體),而是可以從github中的測試代碼中開始。確保@EnableTransactionManagement在創建令牌時,防止在競爭相同行的客戶端應用程序之間發生衝突。還要注意,示例模式有明確的PRIMARY KEY聲明 – 這些在併發環境中也是必需的。

JWT令牌

要使用JWT令牌,您需要JwtTokenStore在授權服務器中。資源服務器還需要能夠對令牌進行解碼,因此它JwtTokenStore具有依賴性JwtAccessTokenConverter,並且授權服務器和資源服務器都需要相同的實現。默認情況下,令牌被簽名,資源服務器還必須能夠驗證簽名,因此它需要與授權服務器(共享密鑰或對稱密鑰)相同的對稱(簽名)密鑰,或者需要公共密鑰(驗證者密鑰),其與授權服務器中的私鑰(簽名密鑰)匹配(公私屬或非對稱密鑰)。公鑰(如果可用)由/oauth/token_key端點上的授權服務器公開,默認情況下,訪問規則為“denyAll()”。AuthorizationServerSecurityConfigurer

要使用JwtTokenStore你需要的“spring-security-jwt”你的類路徑(你可以在與Spring OAuth相同的github倉庫中找到它,但發行週期不同)。

贈款類型

AuthorizationEndpoint可以通過以下方式配置支持的授權類型AuthorizationServerEndpointsConfigurer。默認情況下,所有授權類型均受支持,除了密碼(有關如何切換它的詳細信息,請參見下文)。以下屬性會影響授權類型:

  • authenticationManager:通過注入密碼授權被打開AuthenticationManager
  • userDetailsService:如果您注入UserDetailsService或者全局配置(例如a GlobalAuthenticationManagerConfigurer),則刷新令牌授權將包含對用戶詳細信息的檢查,以確保該帳戶仍然活動
  • authorizationCodeServices:定義AuthorizationCodeServices授權代碼授權的授權代碼服務(實例)。
  • implicitGrantService:在批准期間管理狀態。
  • tokenGranter:(TokenGranter完全控制授予和忽略上述其他屬性)

在XML授予類型中包含作為子元素authorization-server

配置端點URL

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

  • 端點的默認(框架實現)URL路徑
  • 需要的自定義路徑(以“/”開頭)

由框架提供的URL路徑/oauth/authorize(授權端點)/oauth/token(令牌端點)/oauth/confirm_access(用戶發佈批准此處)/oauth/error(用於在授權服務器中呈現錯誤)/oauth/check_token(由資源服務器用於解碼訪問令牌) ,並且/oauth/token_key(如果使用JWT令牌,則公開用於令牌驗證的公鑰)。

注意,授權端點/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
}

注意:如果您的授權服務器也是資源服務器,那麼還有另一個優先級較低的安全過濾器鏈控制API資源。通過訪問令牌來保護這些請求,您需要他們的路徑與主用戶面臨的過濾器鏈中的路徑匹配,因此請務必包含僅在WebSecurityConfigurer上述中選擇非API資源的請求匹配器。

默認情況下,通過Spring OAuth在@Configuration使用客戶機密碼的HTTP Basic認證的支持中為您保護令牌端點。在XML中不是這樣(因此應該明確保護)。

在XML中,<authorization-server/>元素具有一些可以用於以類似方式更改默認端點URL的屬性。該/check_token端點必須(與顯式啟用check-token-enabled屬性)。

自定義UI

大多數授權服務器端點主要由機器使用,但是有一些資源需要一個UI,而這些資源是GET /oauth/confirm_access和HTML響應/oauth/error。它們是在框架中使用白名單實現提供的,因此授權服務器的大多數真實世界實例都希望提供自己的實例,以便他們可以控制樣式和內容。所有您需要做的是@RequestMappings為這些端點提供一個Spring MVC控制器,並且框架默認在調度程序中佔用較低的優先級。在/oauth/confirm_access端點中,您可以期待AuthorizationRequest綁定到會話中,攜帶所有需要用戶查詢的數據(默認的實現是WhitelabelApprovalEndpoint這樣查找起始點複製)。/oauth/authorize您可以從該請求中獲取所有數據,然後根據需要進行渲染,然後所有用戶需要執行的操作都是回覆有關批准或拒絕授權的信息。請求參數直接傳遞給您UserApprovalHandlerAuthorizationEndpoint所以您可以隨便解釋數據。默認UserApprovalHandler取決於您是否已經提供了一個ApprovalStore在你的AuthorizationServerEndpointsConfigurer(在這種情況下,它是一個ApprovalStoreUserApprovalHandler)或不(在這種情況下,它是一個TokenStoreUserApprovalHandler)。標準審批處理程序接受以下內容:默認取決於您是否已經提供了一個在你的(在這種情況下,它是一個)或不(在這種情況下,它是一個)。標準審批處理程序接受以下內容:默認取決於您是否已經提供了一個在你的(在這種情況下,它是一個)或不(在這種情況下,它是一個)。標準審批處理程序接受以下內容:

  • TokenStoreUserApprovalHandler:簡單的是/否決定通過user_oauth_approval等於“真”或“假”。
  • ApprovalStoreUserApprovalHandler:一組scope.*參數鍵與“*”等於所請求的範圍。參數的值可以是“true”或“approved”(如果用戶批准了授權),則該用戶被認為已經拒絕了該範圍。如果批准了至少一個範圍,則贈款是成功的。

注意:不要忘記在您為用戶呈現的表單中包含CSRF保護。默認情況下,Spring Security正期待一個名為“_csrf”的請求參數(它在請求屬性中提供值)。有關更多信息,請參閱Spring Security用戶指南,或查看whitelabel實現的指導。

執行SSL

普通HTTP對於測試是很好的,但授權服務器只能在生產中使用SSL。您可以在安全容器或代理服務器後面運行應用程序,如果正確設置代理和容器(這與OAuth2無關),則應該可以正常運行。您也可能希望使用Spring Security requiresChannel()限制來保護端點。對於/authorize端點,由您來做,作為您正常應用程序安全性的一部分。對於/token端點AuthorizationServerEndpointsConfigurer,可以使用該sslOnly()方法設置一個標誌。在這兩種情況下,安全通道設置是可選的,但是如果Spring Security在不安全的通道上檢測到請求,則會導致Spring Security重定向到安全通道。

自定義錯誤處理

授權服務器中的錯誤處理使用標準Spring MVC功能,即@ExceptionHandler端點本身的方法。用戶還可以向WebResponseExceptionTranslator端點自身提供這些改變響應內容的最佳方式,而不是渲染方式。在授權HttpMesssageConverters端點的情況下,在令牌端點和OAuth錯誤視圖(/oauth/error)的情況下,異常呈現(可以添加到MVC配置中)。該白色標籤錯誤的端點提供了HTML的響應,但用戶可能需要提供自定義實現(如只需添加一個@Controller@RequestMapping("/oauth/error"))。

將用戶角色映射到範圍

限制令牌範圍不僅僅是分配給客戶端的範圍,還可以根據用戶自己的權限來進行限制。如果您在其中使用DefaultOAuth2RequestFactoryAuthorizationEndpoint則可以設置一個標誌checkUserScopes=true,以將允許的範圍限制為僅與那些與用戶角色匹配的範圍。你也可以注入OAuth2RequestFactoryTokenEndpoint但只有工作(即密碼授權),如果你也安裝一個TokenEndpointAuthenticationFilter– 你只需要在HTTP之後添加該過濾器BasicAuthenticationFilter。當然,您還可以實現自己的規則,將作用域映射到角色並安裝自己的版本OAuth2RequestFactory。將AuthorizationServerEndpointsConfigurer讓你注入一個定製的OAuth2RequestFactory,所以你可以使用該功能來建立一個工廠,如果你使用@EnableAuthorizationServer

資源服務器配置

資源服務器(可以與授權服務器或單獨的應用程序相同)提供受OAuth2令牌保護的資源。Spring OAuth提供了實現此保護的Spring Security認證過濾器。您可以@EnableResourceServer@Configuration類上打開它,並使用a進行配置(必要時)ResourceServerConfigurer。可以配置以下功能:

  • tokenServices:定義令牌服務的bean(實例ResourceServerTokenServices)。
  • resourceId:資源的ID(可選,但建議並由驗證服務器驗證,如果存在)。
  • 其他擴展點(例如tokenExtractor從傳入請求中提取令牌)
  • 請求匹配的受保護資源(默認為全部)
  • 受保護資源的訪問規則(默認為“已驗證”)
  • HttpSecuritySpring Security中配置程序允許的受保護資源的其他自定義

@EnableResourceServer註釋添加類型的過濾器OAuth2AuthenticationProcessingFilter自動Spring Security的過濾器鏈。

在XML中有一個<resource-server/>帶有id屬性的元素- 這是一個servlet的bean ID,Filter然後可以手動添加到標準的Spring Security鏈。

ResourceServerTokenServices是與授權服務器的合同的另一半。如果資源服務器和授權服務器在同一個應用程序中,然後使用,DefaultTokenServices那麼您不需要太費心思考,因為它實現了所有必要的接口,因此它自動一致。如果您的資源服務器是一個單獨的應用程序,那麼您必須確保與授權服務器的功能相匹配,並提供一個ResourceServerTokenServices正確的解碼令牌。與授權服務器一樣,您經常可以使用該DefaultTokenServices選項,並且選擇主要通過TokenStore(後端存儲或本地編碼)來表達。RemoteTokenServices一個替代方案是Spring OAuth功能(不是規範的一部分),允許資源服務器通過授權服務器(/oauth/check_token)上的HTTP資源解碼令牌。RemoteTokenServices如果資源服務器中沒有大量的流量(每個請求都必須與授權服務器進行驗證),或者如果能夠緩存結果,那麼它們是方便的。要使用/oauth/check_token端點,您需要通過更改其訪問規則(默認為“denyAll()”)來公開它AuthorizationServerSecurityConfigurer,例如

		@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基本身份驗證的保護。

配置OAuth感知表達式處理程序

您可能希望利用Spring Security 基於表達式的訪問控制。表達式處理程序將默認在@EnableResourceServer安裝程序中註冊。這些表達式包括*#oauth2.clientHasRole*,#oauth2.clientHasAnyRole和*#oath2.denyClient*,可用於根據oauth客戶端的角色提供訪問(請參閱OAuth2SecurityExpressionMethods全面的列表)。在XML中,您可以expression-handler使用常規<http/>安全配置的元素註冊一個oauth感知表達式處理程序。

OAuth 2.0客戶端

OAuth 2.0客戶端機制負責訪問其他服務器的OAuth 2.0保護資源。該配置包括建立用戶可能訪問的相關受保護資源。客戶端還可能需要提供用於存儲用戶的授權碼和訪問令牌的機制。

受保護的資源配置

受保護的資源(或“遠程資源”)可以使用類型的bean定義來定義OAuth2ProtectedResourceDetails。受保護的資源具有以下屬性:

  • id:資源的id。該id僅由客戶端用於查找資源; 它在OAuth協議中從未使用過。它也被用作bean的id。
  • clientId:OAuth客戶端ID。這是OAuth提供商識別您的客戶端的ID。
  • clientSecret:與資源相關的祕密。默認情況下,沒有密碼為空。
  • accessTokenUri:提供訪問令牌的提供者OAuth端點的URI。
  • scope:逗號分隔的字符串列表,指定對資源的訪問範圍。默認情況下,不指定範圍。
  • clientAuthenticationScheme:您的客戶端用於向訪問令牌端點進行身份驗證的方案。建議的值:“http_basic”和“form”。默認值為“http_basic”。請參閱OAuth 2規範的第2.1節。

不同的授權類型具有不同的具體實現OAuth2ProtectedResourceDetails(例如ClientCredentialsResource,對於“client_credentials”授權類型)。對於需要用戶授權的授權類型,還有一個其他屬性:

  • userAuthorizationUri:如果用戶需要授權訪問資源,則用戶將被重定向到的uri。請注意,這並不總是需要,具體取決於支持哪個OAuth 2配置文件。

在XML中有一個<resource/>可以用來創建類型的bean的元素OAuth2ProtectedResourceDetails。它具有匹配上述所有屬性的屬性。

客戶端配置

對於OAuth 2.0客戶端,使用簡化配置@EnableOAuth2Client。這有兩件事情:

  • 創建一個過濾器bean(帶有ID oauth2ClientContextFilter)來存儲當前的請求和上下文。在需要在請求期間進行身份驗證的情況下,管理重定向到和從OAuth認證uri。
  • AccessTokenRequest在請求範圍中創建一個類型的bean 。授權代碼(或隱式)授權客戶端可以使用這種方式來保持與個別用戶的狀態相關。

過濾器必須連接到應用程序中(例如,使用 同一名稱的Servlet初始化程序或web.xml配置DelegatingFilterProxy)。

AccessTokenRequest可以在使用 OAuth2RestTemplate這樣的:

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

在會話範圍中放置OAuth2ClientContext(為您),以保持不同用戶的狀態分開。沒有了,您將不得不自己在服務器上管理等效的數據結構,將傳入的請求映射到用戶,並將每個用戶與單獨的實例相關聯OAuth2ClientContext

在XML中有一個<client/>帶有id屬性的元素- 這是一個servlet的bean id,Filter在這種@Configuration情況下必須映射為一個DelegatingFilterProxy(具有相同名稱)。

訪問受保護的資源

一旦您提供了資源的所有配置,您現在可以訪問這些資源。用於訪問這些資源的建議的方法是通過使用所述RestTemplate在彈簧3引入。Spring Security的OAuth提供只需要提供一個實例的RestTemplate的擴展OAuth2ProtectedResourceDetails。要使用用戶令牌(授權代碼授權),您應該考慮使用創建一些請求和會話作用域上下文對象的@EnableOAuth2Client配置(或XML等效項<oauth:rest-template/>),以便不同用戶的請求在運行時不會相沖突。

作為一般規則,Web應用程序不應使用密碼授權,因此ResourceOwnerPasswordResourceDetails如果可以支持,請避免使用AuthorizationCodeResourceDetails。如果您非常需要從Java客戶端工作的密碼授權,則使用相同的機制來配置您的憑據,並將憑據OAuth2RestTemplate添加到AccessTokenRequest(這是一個Map短暫的),而不是ResourceOwnerPasswordResourceDetails(在所有訪問令牌之間共享)。

在客戶端中持久化令牌

客戶端並不需要堅持令牌,但它可以很好的為不要求用戶每次在客戶端應用程序重新啟動時批准新的代金券授予。該ClientTokenServices接口定義了所必需的持續的OAuth為特定用戶2.0的令牌的動作。提供了一個JDBC實現,但如果您希望實現自己的服務來將持久性數據庫中的訪問令牌和關聯的身份驗證實例存儲起來,那麼您可以使用。如果要使用此功能,您需要提供一個專門配置TokenProviderOAuth2RestTemplate

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

外部OAuth2提供商客戶端的定製

一些外部OAuth2提供者(例如Facebook)不能正確地實現規範,或者他們只是堅持使用舊版本的規範,而不是Spring Security OAuth。要在客戶端應用程序中使用這些提供程序,您可能需要調整客戶端基礎架構的各個部分。

以Facebook為例,應用程序中有一個Facebook功能tonr2(您需要更改配置以添加您自己的,有效的客戶端ID和密碼 – 它們很容易在Facebook網站上生成)。

Facebook令牌響應在令牌的到期時間(它們使用expires而不是expires_in)中也包含不符合規定的JSON條目,因此,如果要在應用程序中使用到期時間,則必須使用自定義手動解碼OAuth2SerializationService

相關文章

通過定時器、時間分片、WebWorker優化長任務

談談Swift中的枚舉內存佈局

ThreadLocal的進化——TransmittableThreadLocal

為什麼每一個爬蟲工程師都應該學習Kafka