应用开发与调优

Spring Security OAuth2 开发指南

2018-11-04  本文已影响103人  一个做java的梦想

Spring Security OAuth2 开发指南

官方文档 :http://projects.spring.io/spring-security-oauth/docs/oauth2.html

授权服务 Authorization Service.
资源服务 Resource Service.

AuthorizationEndpoint:用来作为请求者获得授权的服务,默认的URL是/oauth/authorize. TokenEndpoint:用来作为请求者获得令牌(Token)的服务,默认的URL是/oauth/token.

OAuth2AuthenticationProcessingFilter:用来作为认证令牌(Token)的一个处理流程过滤器。只有当过滤器通过之后,请求者才能获得受保护的资源。

@EnableAuthorizationServer 开启授权服务配置,会配置授权服务必须的端点以及安全配置。
@EnableResourceServer 开启资源服务配置,会配置资源服务相关的安全配置。

一、授权服务配置:

授权服务类型

implicit:隐式授权类型。
password:资源所有者(即用户)密码类型。
client_credentials:客户端凭据(客户端ID以及Key)类型。
refresh_token:通过以上授权获得的刷新令牌来获取新的令牌。

授权服务配置类

用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.
AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。

以上的配置可以选择继承AuthorizationServerConfigurerAdapter并且覆写其中的三个configure方法来进行配置。

想想现在国内各大平台的社会化登陆服务,例如腾讯,用户要使用QQ登录到某个网站,这个网站是跳转到了腾讯的登陆授权页面,然后用户登录并且确定授权之后跳转回目标网站,这种授权方式规范在我上面提供的链接RFC6749的第4.1节有详细阐述。

clientId:(必须的)用来标识客户的Id。
secret:(需要值得信任的客户端)客户端安全码,如果有的话。
scope:用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。 authorizedGrantTypes:此客户端可以使用的授权类型,默认为空。
authorities:此客户端可以使用的权限(基于Spring Security authorities)。

客户端详情(Client Details)

管理令牌(Managing Token)

1)当一个令牌被创建了,你必须对其进行保存,这样当一个客户端使用这个令牌对资源服务进行请求的时候才能够引用这个令牌。
2)当一个令牌是有效的时候,它可以被用来加载身份信息,里面包含了这个令牌的相关权限。
3)当你自己创建 AuthorizationServerTokenServices这个接口的实现时,你可能需要考虑一下使用 DefaultTokenServices这个类,里面包含了一些有用实现,你可以使用它来修改令牌的格式和令牌的存储。默认的,当它尝试创建一个令牌的时候,是使用随机值来进行填充的,除了持久化令牌是委托一个 TokenStore接口来实现以外,这个类几乎帮你做了所有的事情。并且 TokenStore 这个接口有一个默认的实现,它就是InMemoryTokenStore,如其命名,所有的令牌是被保存在了内存中。除了使用这个类以外,你还可以使用一些其他的预定义实现,下面有几个版本,它们都实现了TokenStore接口:

InMemoryTokenStore:这个版本的实现是被默认采用的,它可以完美的工作在单服务器上(即访问并发量压力不大的情况下,并且它在失败的时候不会进行备份),大多数的项目都可以使用这个版本的实现来进行尝试,你可以在开发的时候使用它来进行管理,因为不会被保存到磁盘中,所以更易于调试。

JdbcTokenStore:这是一个基于JDBC的实现版本,令牌会被保存进关系型数据库。使用这个版本的实现时,你可以在不同的服务器之间共享令牌信息,使用这个版本的时候请注意把"spring-jdbc"这个依赖加入到你的classpath当中。

JwtTokenStore:这个版本的全称是 JSON Web Token(JWT),它可以把令牌相关的数据进行编码(因此对于后端服务来说,它不需要进行存储,这将是一个重大优势),但是它有一个缺点,那就是撤销一个已经授权令牌将会非常困难,所以它通常用来处理一个生命周期较短的令牌以及撤销刷新令牌(refresh_token)。另外一个缺点就是这个令牌占用的空间会比较大,如果你加入了比较多用户凭证信息。JwtTokenStore 不会保存任何数据,但是它在转换令牌值以及授权信息方面与 DefaultTokenServices 所扮演的角色是一样的。

JWT令牌(JWT Tokens)

/**
 * 授权服务器端点的安全配置
 */
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    oauthServer
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("permitAll()")
            // 让/oauth/token支持client_id以及client_secret作登录认证
            .allowFormAuthenticationForClients();
}

如果你要使用 JwtTokenStore,请务必把"spring-security-jwt"这个依赖加入到你的classpath中。

配置授权类型(Grant Types)

authenticationManager:认证管理器,当你选择了资源所有者密码(password)授权类型的时候,请设置这个属性注入一个 AuthenticationManager 对象。

userDetailsService:如果你设置了这个属性的话,那说明你有一个自己的UserDetailsService 接口的实现,或者你可以把这个东西设置到全局域上面去(例如GlobalAuthenticationManagerConfigurer 这个配置对象),当你设置了这个之后,那么"refresh_token"即刷新令牌授权类型模式的流程中就会包含一个检查,用来确保这个账号是否仍然有效,假如说你禁用了这个账户的话。

authorizationCodeServices:这个属性是用来设置授权码服务的(即AuthorizationCodeServices 的实例对象),主要用于 "authorization_code" 授权码类型模式。

mplicitGrantService:这个属性用于设置隐式授权模式,用来管理隐式授权模式的状态。

tokenGranter:这个属性就很牛B了,当你设置了这个东西(即TokenGranter接口实现),那么授权将会交由你来完全掌控,并且会忽略掉上面的这几个属性,这个属性一般是用作拓展用途的,即标准的四种授权模式已经满足不了你的需求的时候,才会考虑使用这个。

配置授权端点的URL(Endpoint URLs):

第一个参数:String 类型的,这个端点URL的默认链接。
第二个参数:String 类型的,你要进行替代的URL链接。

/oauth/authorize:授权端点。
/oauth/token:令牌端点。
/oauth/confirm_access:用户确认授权提交端点。
/oauth/error:授权服务错误信息端点。
/oauth/check_token:用于资源服务访问的令牌解析端点。
/oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        .antMatchers("/login").permitAll()
        //default protection for all resources (including /oauth/authorize)
        .and().authorizeRequests().anyRequest().hasRole("USER")
        //... more configuration, e.g. for form login
}
 注意:如果你的应用程序中既包含授权服务又包含资源服务的话,那么这里实际上是另一个的低优先级的过滤器来控制资源接口的,
 这些接口是被保护在了一个访问令牌(access token)中, 所以请挑选一个URL链接来确保你的资源接口中有一个不需要被保护的链接用来取得授权,
 就如上面示例中的 /login 链接,你需要在 WebSecurityConfigurer 配置对象中进行设置。

强制使用SSL(Enforcing SSL):

自定义错误处理(Error Handling):

映射用户角色到权限范围(Mapping User Roles to Scopes):

二、资源服务配置:

tokenServices:ResourceServerTokenServices 类的实例,用来实现令牌服务。
resourceId:这个资源服务的ID,这个属性是可选的,但是推荐设置并在授权服务中进行验证。
tokenExtractor 令牌提取器用来提取请求中的令牌。
请求匹配器,用来设置需要进行保护的资源路径,默认的情况下是受保护资源服务的全部路径。
受保护资源的访问规则,默认的规则是简单的身份验证(plain authenticated)。
其他的自定义权限保护规则通过 HttpSecurity 来进行配置。

使用授权服务的/oauth/check_token端点你需要将这个端点暴露出去,以便资源服务可以进行访问,这在咱们授权服务配置中已经提到了,下面是另外一个例子:

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
    oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')")
        .checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}

配置OAuth-Aware表达式处理器(OAuth-Aware Expression Handler):

OAuth2.0 客户端

受保护资源的配置

   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节。
userAuthorizationUri: 用户访问资源需要身份验证时跳转页面的URI。 注意这个字段不是必填的,它依赖于被支持的OAuth 2的配置文件类型。
创建一个过滤bean(用oauth2ClientContextFilter)来存储当前的请求和上下文。在这种情况下需要在请求期间授权,来管理从重定向的位置和OAuth已认证的url。
在请求作用域中创建一个AccessTokenRequest类型的bean。这个类型的bean可以在个人用户的冲突中被识别码识别(或隐式的),授权客户端保持授权状态。
@Autowired
private OAuth2ClientContext oauth2Context;

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

访问受保护的资源

客户端持久化访问令牌

@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 提供者定制客户端

上一篇下一篇

猜你喜欢

热点阅读