Spring Security 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请求过滤器处理。为了实现OAuth 2.0授权服务器,Spring Security过滤器链中需要以下端点:
-
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
不是一个真正的“商店”,因为它不会保留任何数据,但它在翻译令牌值和验证信息之间起着相同的作用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
或者全局配置(例如aGlobalAuthenticationManagerConfigurer
),则刷新令牌授权将包含对用户详细信息的检查,以确保该帐户仍然活动 -
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
您可以从该请求中获取所有数据,然后根据需要进行渲染,然后所有用户需要执行的操作都是回复有关批准或拒绝授权的信息。请求参数直接传递给您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在不安全的通道上检测到请求,则会导致Spring Security重定向到安全通道。
自定义错误处理
授权服务器中的错误处理使用标准Spring MVC功能,即@ExceptionHandler
端点本身的方法。用户还可以向WebResponseExceptionTranslator
端点自身提供这些改变响应内容的最佳方式,而不是渲染方式。在授权HttpMesssageConverters
端点的情况下,在令牌端点和OAuth错误视图(/oauth/error
)的情况下,异常呈现(可以添加到MVC配置中)。该白色标签错误的端点提供了HTML的响应,但用户可能需要提供自定义实现(如只需添加一个@Controller
带@RequestMapping("/oauth/error")
)。
将用户角色映射到范围
限制令牌范围不仅仅是分配给客户端的范围,还可以根据用户自己的权限来进行限制。如果您在其中使用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(可选,但建议并由验证服务器验证,如果存在)。 - 其他扩展点(例如
tokenExtractor
从传入请求中提取令牌) - 请求匹配的受保护资源(默认为全部)
- 受保护资源的访问规则(默认为“已验证”)
-
HttpSecurity
Spring 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实现,但如果您希望实现自己的服务来将持久性数据库中的访问令牌和关联的身份验证实例存储起来,那么您可以使用。如果要使用此功能,您需要提供一个专门配置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为例,应用程序中有一个Facebook功能tonr2
(您需要更改配置以添加您自己的,有效的客户端ID和密码 - 它们很容易在Facebook网站上生成)。
Facebook令牌响应在令牌的到期时间(它们使用expires
而不是expires_in
)中也包含不符合规定的JSON条目,因此,如果要在应用程序中使用到期时间,则必须使用自定义手动解码OAuth2SerializationService
。