OAuth 2开发人员指南
介绍
这是支持的用户指南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请求过滤器处理。Spring Security过滤器链中需要以下端点才能实现OAuth 2.0授权服务器:
- AuthorizationEndpoint用于服务授权请求。默认网址:/oauth/authorize。
- TokenEndpoint用于服务访问令牌的请求。默认网址:/oauth/token。
要实现OAuth 2.0资源服务器,需要以下过滤器:
- 将OAuth2AuthenticationProcessingFilter用于加载给定的认证访问令牌请求的认证。
对于所有OAuth 2.0提供程序功能,使用特殊的Spring OAuth @Configuration适配器简化了配置。OAuth配置还有一个XML命名空间,架构位于http://www.springframework.org/schema/security/spring-security-oauth2.xsd。命名空间是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是不是一个真正的“商店”,因为它不会保留任何数据的意义,但是它扮演翻译的betweeen令牌值和认证信息相同的角色DefaultTokenServices。
注意:JDBC服务的模式不与库打包在一起(因为您可能希望在实践中使用太多变体),但是有一个示例可以从github中的测试代码开始。确保@EnableTransactionManagement在创建令牌时防止竞争相同行的客户端应用程序之间发生冲突。另请注意,示例模式具有显式PRIMARY KEY声明 - 这些声明在并发环境中也是必需的。
JWT代币
要使用JWT令牌,您需要JwtTokenStore在授权服务器中使用。资源服务器还需要能够解码令牌,因此JwtTokenStore它依赖于a JwtAccessTokenConverter,并且授权服务器和资源服务器都需要相同的实现。默认情况下,令牌是签名的,资源服务器也必须能够验证签名,因此它需要与授权服务器(共享密钥或对称密钥)相同的对称(签名)密钥,或者它需要公共与授权服务器中的私钥(签名密钥)匹配的密钥(验证者密钥)(公共 - 私有或非对称密钥)。公共密钥(如果可用)由授权服务器公开/oauth/token_key端点,默认情况下是安全的,具有访问规则“denyAll()”。你可以通过注入一个标准的SpEL表达式来打开它AuthorizationServerSecurityConfigurer(例如“permitAll()”可能就足够了,因为它是一个公钥)。
要JwtTokenStore在类路径中使用“spring-security-jwt”(您可以在与Spring OAuth相同的github存储库中找到它,但具有不同的发布周期)。
拨款类型
AuthorizationEndpoint可以通过以下方式配置支持的授权类型AuthorizationServerEndpointsConfigurer。默认情况下,除密码外都支持所有授权类型(有关如何打开密码的详细信息,请参阅下文)。以下属性会影响授权类型:
- authenticationManager:密码授予通过注入来打开AuthenticationManager。
- userDetailsService:如果您注入一个UserDetailsService或者无论如何都是全局配置的(例如,在a中GlobalAuthenticationManagerConfigurer),那么刷新令牌授权将包含对用户详细信息的检查,以确保该帐户仍处于活动状态
- authorizationCodeServices:定义授权代码授权的授权代码服务(实例AuthorizationCodeServices)。
- implicitGrantService:在imlpicit授权期间管理状态。
- 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基本身份验证为您保护令牌端点。这不是XML中的情况(因此应该明确地保护它)。
在XML中,<authorization-server/>元素具有一些属性,可用于以类似的方式更改默认端点URL。该/check_token端点必须(与显式启用check-token-enabled属性)。
自定义UI
大多数Authorization Server端点主要由机器使用,但是有一些资源需要UI,而这些资源是GET for /oauth/confirm_access和HTML响应/oauth/error。它们是在框架中使用whitelabel实现提供的,因此授权服务器的大多数实际实例都希望提供自己的实例,以便它们可以控制样式和内容。您需要做的就是@RequestMappings为这些端点提供Spring MVC控制器,并且框架默认值在调度程序中的优先级较低。在/oauth/confirm_access端点中,您可以期望AuthorizationRequest绑定到会话,其中包含寻求用户批准所需的所有数据(默认实现是WhitelabelApprovalEndpoint所以在那里找一个复制的起点)。您可以从该请求中获取所有数据并根据需要进行渲染,然后用户需要做的就是POST回到/oauth/authorize有关批准或拒绝授权的信息。请求参数直接传递给a UserApprovalHandler,AuthorizationEndpoint因此您可以根据需要或多或少地解释数据。默认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重定向到它认为的安全通道。
自定义错误处理
Authorization Server中的错误处理使用标准的Spring MVC功能,即@ExceptionHandler端点本身的方法。用户还可以WebResponseExceptionTranslator向端点提供自身,这是更改响应内容的最佳方式,而不是呈现它们的方式。HttpMesssageConverters在令牌端点的情况下,异常的呈现委托给(可以添加到MVC配置),在/oauth/error授权端点的情况下委托给OAuth错误视图()。该白色标签错误的端点提供了HTML的响应,但用户可能需要提供自定义实现(如只需添加一个@Controller带@RequestMapping("/oauth/error"))。
将用户角色映射到范围
有时限制令牌的范围不仅取决于分配给客户端的作用域,还取决于用户自己的权限。如果您使用a DefaultOAuth2RequestFactory,则AuthorizationEndpoint可以设置一个标志checkUserScopes=true,将允许的范围限制为仅匹配用户角色的范围。您也可以注入OAuth2RequestFactory到TokenEndpoint只有工作,但(即密码赠款),如果您还安装TokenEndpointAuthenticationFilter-你只需要HTTP后添加过滤器BasicAuthenticationFilter。当然,您也可以实现自己的规则来将范围映射到角色并安装自己的版本OAuth2RequestFactory。将AuthorizationServerEndpointsConfigurer让你注入一个定制的OAuth2RequestFactory,所以你可以使用该功能来建立一个工厂,如果你使用@EnableAuthorizationServer。
资源服务器配置
资源服务器(可以与授权服务器或单独的应用程序相同)提供受OAuth2令牌保护的资源。Spring OAuth提供了一个实现此保护的Spring Security身份验证过滤器。您可以@EnableResourceServer在@Configuration类上打开它,并使用a配置它(根据需要)ResourceServerConfigurer。可以配置以下功能:
- tokenServices:定义令牌服务的bean(实例ResourceServerTokenServices)。
- resourceId:资源的id(可选,但建议使用,如果存在,将由auth服务器验证)。
- resourecs服务器的其他扩展点(例如,tokenExtractor用于从传入请求中提取令牌)
- 请求受保护资源的匹配器(默认为所有)
- 受保护资源的访问规则(默认为plain“authenticated”)
- HttpSecuritySpring Security中configurer 允许的受保护资源的其他自定义
该@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-Aware表达式处理程序
您可能希望利用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情况下映射到a 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实现,但是如果您希望实现自己的服务来在持久性数据库中存储访问令牌和关联的身份验证实例,则可以使用它。如果您想使用此功能,您需要提供专门配置TokenProvider的OAuth2RestTemplate例如
@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为例,tonr2应用程序中有一个Facebook功能(您需要更改配置以添加您自己的,有效的,客户端ID和秘密 - 它们很容易在Facebook网站上生成)。
Facebook令牌响应还包含一个不合规的JSON条目,用于令牌的到期时间(它们使用expires而不是expires_in),因此如果您想在应用程序中使用到期时间,则必须使用自定义手动解码它OAuth2SerializationService。