SpringSecurityOAuth2开发指南中⽂版
介绍
这是 的⽤户指南。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 的提供⽅实际涵盖两个⾓⾊,即认证服务 (Authorization Service) 和资源服务 (Resource Service),有时候它们会在同⼀个应⽤程序中实现。使⽤ Spring Security OAuth 的时候你可以选择把把
它们分别放在两个应⽤程序中,也可以选择建⽴使⽤同⼀个认证服务的多个资源服务。对令牌的请求由 Spring MVC 控制器终端进⾏处理,⽽标准的 Spring security 请求过滤器会处理对受保护资源的访问。Spring以 Security 过滤器链需要以下各端来实现 OAuth 2.0 认证服务:
服务于认证请求。默认 URL: /oauth/authorize。
服务于访问令牌的请求。默认 URL: /oauth/token。
下⾯的过滤器⽤于实现 OAuth 2.0 资源服务:
⽤来对请求给出的⾝份令牌加载认证。
授权服务器配置
在配置授权服务器时,您必须考虑客户端⽤来从最终⽤户获取访问令牌的授权类型(例如,授权码,⽤户凭证,刷新令牌)。服务器的配置⽤于提供客户端详细信息服务和令牌服务的实现,并启⽤或禁⽤全局机制的某些⽅⾯。但是请注意,每个客户端都可以配置特定的权限,以便能够使⽤某些授权机制和访问权限。即仅仅因为您的提供程序配置为⽀持“客户端凭据”授予类型,并不意味着特定的客户端有权使⽤该授予类型。
@EnableAuthorizationServer 注解⽤于配置 OAuth 2.0 授权服务器机制,以及实现 AuthorizationServerConfigurer 的任何 @Beans (有⼀个使⽤空⽅法的⽅便的适配器实现)。以下功能委托给由 Spring 创建并传递给 AuthorizationServerConfigurer 的独⽴配置器:
ClientDetailsServiceConfigurer:定义客户端详细信息服务的配置器。客户详细信息可以初始化,或者可以引⽤现有的 store
AuthorizationServerSecurityConfigurer:定义令牌端点上的安全约束
AuthorizationServerEndpointsConfigurer:定义授权和令牌端点以及令牌服务
提供程序配置的⼀个重要⽅⾯是将授权代码提供给 OAuth 客户端(在授权代码授权中)的⽅式。OAuth 客户端通过将最终⽤户导向授权页⾯来获得授权码,其中⽤户可以输⼊其证书,导致从授权服务器重定向到具有授权码的 OAuth 客户端。OAuth 2 规范详细阐述了这⽅⾯的例⼦。
在 XML 中,有⼀个 <authorization-server/> 元素⽤于配置 OAuth 2.0 授权服务器。
客户端配置详细说明
ClientDetailsServiceConfigurer (来⾃ AuthorizationServerConfigurer 的⼀个回调)可⽤于定义客户端细节服务中的内存或 JDBC 实现。客户端重要的属性是:
clientId: (必须的)客户端 id
secret: (要求⽤于受信任的客户端)客户端的机密,如果有的话web后端是指什么
scope: 客户范围限制。如果范围未定义或为空(默认),客户端将不受范围限制
authorizedGrantTypes: 授权客户端使⽤的授予类型。默认值为空
authorities: 授权给客户的认证(常规 Spring Security 认证)
通过直接访问底层存储(例如 JdbcClientDetailsService ⽤例中的数据库表)或者通过 ClientDetailsManager 接⼝
(ClientDetailsService 也能实现这两种实现),可以在正在运⾏的应⽤程序中更新客户端详细信息。
注意:JDBC 服务的 schema 未与库打包(因为在实践中可能会有太多的变化),但是你可以从 这个例⼦开始。
管理 Token
接⼝定义了管理 OAuth 2.0 token 的必要操作。请注意以下⼏点:
当创建⼀个可访问 Token 时, ⾝份认证必须存储起来,以便接受可访问 Token 的资源后⾯可作为引⽤。
可访问的 Token ⽤来加载授权创建的⾝份验证。
当你创建了 AuthorizationServerTokenServices 的实现后,你可能会考虑使⽤ ,这个接⼝中有多种⽅法,可以⽤来改变访问 Token 的格式和存储。默认情况下使⽤随机值创建 token 并处理所有的内容,除了委托给 TokenStore 的 token 持久化操作。默认存储⽅式是,不过也⽀持另外的实现⽅式。下⾯为你介绍其他实现⽅式。
默认的 InMemoryTokenStore 在单个服务器上表现良好(少量传输且失败时不通过热插拔更换到备份服务器)。多数项⽬都可以从它开始,也可以在开发模式中使⽤,它可以直接启动,没有其它依赖。
JdbcTokenStore 是同⼀个东西,不过是 ,它把数据保存在关系型数据库中。使⽤ JDBC 版本可以实现在多个服务器上共享数据库,如果只有⼀台服务器也可以直接在上⾯部署服务,或者在有多个组件的情况下使⽤认证和资源服务器。要使⽤ JdbcTokenStore,需要在 classpath 中配置 "spring-jdbc"。
将所有授权相关的数据通过编码后存储起来(没使⽤后端存储是其显著优势)。它的缺点是不能轻易地撤消访问令牌,因此它们的有效时间通常都很短,在更新令牌的时候处理撤消。还有⼀个缺点是,
如果令牌存储了⼤量的⽤户凭据信息,它就可能变得相当⼤。不过在 DefaultTokenServices 中,它起着翻译令牌值和认证信息的作⽤。
注意:针对 JDBC 服务的架构所需要的库并未打包在其中(因为在实际使⽤中有太多不确定因素),不过你可以把 作为⽰例,从这⾥开始。⼀定要使⽤ @EnableTransactionManagement 来防⽌在多个客户端创建令牌时产⽣⾏数据冲突。还要注意⽰例架构有明确的主键(PRIMARY KEY)声明——这些在开发环境中也是必需的。
JWT 令牌
需要在认证服务器中加⼊ JwtTokenStore 来⽀持 JWT 令牌。资源服务器要能对令牌进⾏解码,所以 JwtTokenStore 会依赖JwtAccessTokenConverter,⽽且认证服务器和资源服务器要有相同的实现。默认情况下令牌会有签名,⽽资源服务器也要能验证印鉴,所以它需要与授权服务器相同的对称(签名)密钥(共享密钥或对称密钥),或者,它需要⼀个与认证服务器所使⽤的私钥配对的公钥(验证密钥)(公私密钥或⾮对称密钥)。公钥(如果有的话)会由认证服务器通过 /oauth/token_key 提供,这个地址默认情况下是通过“deenyAll()” 来保护的。你可以向 AuthorizationServerSecurityConfigurer 注⼊⼀个标准的 SpEL 表达式(⽐如
“permitAll()” 就可以,因为那是公钥)来放开保护限制。
使⽤ JwtTokenStore 的时候你需要在 classpath 中有 "spring-security-jwt"(你可以在 Spring OAuth 所在的 github 库的另⼀个发布周期中到)。
授予类型
AuthorizationEndpoint ⽀持的授权类型可以通过 AuthorizationServerEndpointsConfigurer 进⾏配置。 默认情况下,除了密码之外,所有的授权类型都是受⽀持的(请参阅下⾯的关于如何打开的细节)。 以下属性影响授权类型:
authenticationManager:通过注⼊ AuthenticationManager 来开启密码授权。
userDetailsService:如果你注⼊⼀个 UserDetailsService,或者全局地配置了⼀个UserDetailsService(例如在
GlobalAuthenticationManagerConfigurer中),那么刷新令牌授权将包含对⽤户详细信息的检查,以确保该帐户仍然是活动的authorizationCodeServices:为授权代码授权定义授权代码服务(AuthorizationCodeServices 的实例)。
implicitGrantService:在 imlpicit 授权期间管理状态。
tokenGranter:TokenGranter(完全控制授予和忽略上⾯的其他属性)
在 XML 中,授权类型作为授权服务器的⼦元素被包含在内。
配置端点的 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:
1. @Override
2. protected void configure(HttpSecurity http) throws Exception {
3. http
4. .authorizeRequests().antMatchers("/login").permitAll().and()
5. // default protection for all resources (including /oauth/authorize)
6. .authorizeRequests()
7. .anyRequest().hasRole("USER")
8. // ... more configuration, e.g. for form login
9. }
注意:如果您的授权服务器也是⼀个资源服务器,那么另⼀个安全筛选器链只具有较低的优先级来控制 API 资源。如果这些请求被访问令牌所保护,那么您需要的路径就不能与主⽤户⾯对的过滤器链中的路径相匹配,因此请确保在请求匹配器中仅包含上述
WebSecurityConfigurer 中的⾮ API 资源。
默认情况下,Spring OAuth 在使⽤客户端密钥的 HTTP 基本⾝份验证的 @Configuration ⽀持中为您保护令牌端点。不过它不适⽤于使⽤ XML 的情况(所以它应该被明确定义)。
在 XML 中, 元素具有⼀些可⽤于以类似⽅式更改默认端点 URL 的属性。 所以必须显式启⽤ /check_token 端点(启⽤ check-token-enabled 属性)。
⾃定义⽤户界⾯
⼤部分的授权服务终端接⼝是被机器使⽤的,但是也总有⼏个资源需要通过界⾯ UI 的,通过 GET /oauth/confirm_access 获取的页⾯和/oauth/error 返回的 html。框架使⽤⽩板(whitelabel)来提供界⾯,所以现实中,⼤部分的授权服务需要提供⾃⼰的界⾯,这样才能控制界⾯样式和内容。你所需要做的就是提供⼀个 springmvc 的 controller,给这个终端接⼝加上 @RequestMappings,框架默认采⽤低优先权的分发器(dispatcher)。在 /oauth/confirm_access 终端接⼝,你期望⼀个 AuthorizationRequest 绑定到 session,session 携带所有需要⽤户批准的数据(默认的实现是 WhitelabelApprovalEndpoint,所以以这个为 copy 的起点),你能取得在 request 中的所有数据并且按你喜欢的样⼦渲染处来。然后⽤户需要做的就是带着授权或拒绝权限的信息 POST 请求到 /oauth/authorize。请求的额参数通过 AuthorizationEndpoint 被直接传输到 UserApprovalHandler,因此你能够按你需要的解析这些数据。默认的UserApprovalHandler 依赖于你是否在 AuthorizationServe
rEndpointsConfigurer 提供 ApprovalStore(提供的情况下是ApprovalStoreUserApprovalHandler)(没有提供的情况下是 TokenStoreUserApprovalHandler)。
标准的授权 handlers 按如下接受:
TokenStoreUserApprovalHandler:通过判断 user_oauth_approval 等于 true 或 false,简单的返回 yes 或 no。
ApprovalStoreUserApprovalHandler:⼀组参数的 key 为 scope,.*,这些 key 的“*”等于被请求的域(scope)。参数的值是 true 或者 approved(如果⽤户授权了权限),否则⽤户被认为在这个域(scope)被拒绝。如果最终有⼀个域(scope)被授权,那么权限授权就是成功的。
注意:不要忘记加⼊ CSRF 来保护你渲染给⽤户的表单页⾯。Spring Security 默认期望参数中有⼀个名为“_csrf"的参数(Spring Security 在请求属性中提供该值)。查看 Spring Security ⽤户⼿册获取更多相关信息,或者查看 whitelabel 实现作为指导。
强制使⽤ SSL**
普通的 HTTP ⽤于测试是⾜够的,但授权服务器在⽣产环境中应只使⽤ SSL。你可以在安全的容器中或在代理之后运⾏应⽤程序,如果你正确设置代理和容器(这与 OAuth2 ⽆关),它应该可以正常⼯
作。你可能还想要使⽤ Spring Security 的 requiresChannel() 约束来保护终端。对 /authorize 端点是由你来做到这⼀点,作为你的正常应⽤程序的安全性的⼀部分。对于 /token 端
点,AuthorizationServerEndpointsConfigurer 中有⼀个可以配置使⽤ sslOnly() ⽅法的标志。在这两种情况下,安全通道设置都是可选的,但是如果它在不安全的通道上检测到请求,将会导致 Spring Security 重定向到它认为是安全的通道。
⾃定义错误处理**
授权服务器中的错误处理使⽤标准的 Spring MVC 功能,即端点本⾝的 @ExceptionHandler ⽅法。⽤户还可以向端点本⾝提供WebResponseExceptionTranslator,这是改变响应内容的最佳⽅式,⽽不是被渲染的⽅式。在授权端点的情况下,异常的渲染可委托给HttpMesssageConverters(可以添加到 MVC 配置)渲染,并且对于 teh 授权终端,可委托给 OAuth 错误视图(/oauth/error)渲染。 为 HTML 响应提供了⽩标签错误端点,但⽤户可能需要提供⼀个⾃定义的实现(例如,只需添加⼀个⼀个带
有 @RequestMapping("/oauth/error") 的 @Controller)。
将⽤户⾓⾊映射到 Scope 上
有时限制 Tokens 的 scope 不限于分配给客户端的 Scope ,还参考⽤户⾃⼰的权限是⾮常有⽤的。如果你在你的AuthorizationEndpoint 中使⽤ DefaultOAuth2RequestFactory ,则可以设置⼀个 checkUserScopes=true 标签,以将允许的scope 限制为仅与⽤户⾓⾊相匹配的 scope 。你也可以在 TokenEndpoint 中插⼊⼀个 OAuth2RequestFactory ,但这仅在安装了TokenEndpointAuthenticationFilter 的情况下才能⼯作(例如,使⽤密码授权时)——你只需要在 HTTP BasicAuthenticationFilter
之后添加该过滤器即可。当然,你也可以实现⾃⼰的规则,将 scope 映射到⾓⾊,并安装你⾃⼰的 OAuth2RequestFactory 版本。AuthorizationServerEndpointsConfigurer 允许你注⼊⼀个⾃定义的 OAuth2RequestFactory ,以便你可以在使⽤
@EnableAuthorizationServer 时⽤该功能来配置 factory 。
资源服务器配置
资源服务器(可以与授权服务器或单独的应⽤程序相同)为受 OAuth2 令牌保护的资源提供服务。Spring OAuth 提供了⼀个实现这种保护的 Spring Security 认证过滤器。您可以在 Configuration 类上使⽤ @EnableResourceServer 将其打开,并使⽤ResourceServerConfigurer 对其进⾏配置(如有必要)。以下功能可以配置:
tokenServices:定义令牌服务的 bean(ResourceServerTokenServices 的实例)。
resourceId:资源的 id(可选,但是推荐,并且将由 auth 服务器验证,如果存在的话)。
资源服务器的其他扩展点(例如 tokenExtractor ⽤于从传⼊请求中提取令牌)
请求受保护资源的匹配器(默认为全部)
受保护资源的访问规则(默认为普通的"authenticated")
Spring Security 中 HttpSecurity 配置器所允许的受保护资源的其他⾃定义情况
@EnableResourceServer 批注⾃动向 Spring Security 筛选器链添加⼀个 OAuth2AuthenticationProcessingFilter 类型的筛选器。在 XML 中有⼀个带有 id 属性的 元素 - 这是 servlet Filter 的 bean id,然后可以⼿动添加到标准 Spring Security 链。
您的 ResourceServerTokenServices 是授权服务器的另⼀半合同。如果资源服务器和授权服务器在同⼀个应⽤程序中,并且您使⽤了DefaultTokenServices,那么您不必过多考虑这⼀点,因为它实现了所有必要的接⼝,因此它⾃动保持⼀致。如果您的资源服务器是⼀个单独的应⽤程序,那么您必须确保您匹配授权服务器的功能,并提供知道如何正确解码令牌的 ResourceServerTokenServices。与授权
服务器⼀样,您通常可以使⽤ DefaultTokenServices,⽽选择主要通过 TokenStore(后端存储或本地编码)表⽰。另⼀种⽅法是RemoteTokenServices,它是 Spring OAuth 功能(不是规范的⼀部分),允许资源服务器通过授权服务器(/ oauth / check_token)上的 HTTP 资源来解码令牌。 如果资源服务器中没有⼤量流量(每个请求必须使⽤授权服务器进⾏验证),或者您可以负担缓存结果,则RemoteTokenServices ⽅便。要使⽤ / oauth / check_token 端点,您需要通过更改 AuthorizationServerSecurityConfigurer 中的访问规则(默认值为“denyAll()”)来公开它。
1. @Override
2. public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
3. kenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
4. "hasAuthority('ROLE_TRUSTED_CLIENT')");
5. }
在这个例⼦中,我们配置 / oauth / check_token 端点和 / oauth / token_key 端点(所以可信资源可以获得 JWT 验证的公钥)。这两个端点受到使⽤客户端凭据的 HTTP 基本⾝份验证的保护。
配置 OAuth-Aware 表达式处理程序
你可能想要利⽤ Spring Security 的。表达式处理程序将默认注册在 @EnableResourceServer 设置中。表达式包
含 *#oauth2.clientHasRole*, *#oauth2.clientHasAnyRole* 和 *#oath2.denyClient* ,可⽤于根据 oauth 客户端的⾓⾊提供访问控制(请参阅 OAuth2SecurityExpressionMethods 以获取全⾯的列表)。在 XML 中,你可以使⽤常规 安全配置的表达式处理程序元素注册⼀个 oauth-aware 表达式处理程序。
OAuth 2.0 客户端
OAuth 2.0 客户端机制负责访问受 OAuth 2.0 保护的其他服务器的资源。配置涉及建⽴⽤户可能访问的相关受保护资源。客户端可能还需要提供⽤于存储授权代码和⽤户访问令牌的机制。
受保护的资源配置
受保护的资源(或“远程资源”)可以使⽤ OAuth2ProtectedResourceDetails 类型的 bean 定义来定义。受保护的资源具有以下属性:
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 的具体实现(例如“client_credentials”授权类型的ClientCredentialsResource)。对于需要⽤户授权的认证类型,还有⼀个属性:
userAuthorizationUri:如果⽤户需要授权访问资源,⽤户将被重定向到的 URI。请注意,这并不总是必需的,具体取决于⽀持哪些OAuth 2 配置⽂件。
在 XML 中,有⼀个 元素可⽤于创建 OAuth2ProtectedResourceDetails 类型的 bean。它具有与上述所有属性匹配的属性。
客户端配置
对于 OAuth 2.0 客户端,使⽤ @EnableOAuth2Client 进⾏配置是⾮常简单的。需要做两件事情:
创建⼀个过滤器 bean(ID 为 oauth2ClientContextFilter)来存储当前请求和上下⽂。在请求期间需要进⾏⾝份验证的情况下,它将管理 OAuth ⾝份验证 URI 中的重定向。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论