Spring Security OAuth 2.0

·前一篇《OAuth 2.0html

OAuth 2.0 Provider 实现

在OAuth 2.0中,provider角色事实上是把受权服务和资源服务分开,有时候它们也可能在同一个应用中,用Spring Security OAuth你能够选择把它们分红两个应用,固然多个资源服务能够共享同一个受权服务。java

获取token的请求由Spring MVC的控制端点处理,访问受保护的资源由标准的Spring Security请求过滤器处理。git

(The requests for the tokens are handled by Spring MVC controller endpoints, and access to protected resources is handled by standard Spring Security request filters. )github

为了实现OAuth 2.0受权服务器,在Spring Security的过滤器链中须要有如下端点:web

  • AuthorizationEndpoint    用于服务受权请求。默认URL/oauth/authorize
  • TokenEndpoint    用于服务访问令牌请求。默认URL/oauth/token

OAuth 2.0的资源服务器中须要实现下列过滤器:spring

  • OAuth2AuthenticationProcessingFilter    用于加载认证

对于全部的OAuth 2.0 provider特性,最简单的配置是用Spring OAuth @Configuration适配器。sql

Authorization Server 配置

只要你配置了受权服务器,那么你应该考虑客户端用于获取access token的受权类型(例如,受权码,用户凭证,刷新token)。数据库

服务器的配置是用来提供client detail服务和token服务的,而且能够启用或者禁用全局的某些机制。后端

每一个客户端能够配置不一样的权限api

@EnableAuthorizationServer注解被用来配置受权服务器,也能够和实现了AuthorizationServerConfigurer接口的任意被标记为@Bean的Bean一块儿来对受权服务器进行配置。

下列特性被委托给AuthorizationServerConfigurer:

  • ClientDetailsServiceConfigurer  :a configurer that defines the client details service
  • AuthorizationServerSecurityConfigurer  :defines the security constraints on the token endpoint
  • AuthorizationServerEndpointsConfigurer  :defines the authorization and token endpoints and the token services

一件重要的事情是,provider配置了将受权码给OAuth客户端的方式(PS:在受权码类型受权过程当中)

OAuth客户端经过将end-user(最终用户)导向受权页,用户可用在此输入他的凭证。以后,受权服务器携带受权码经过重定向的方式将受权码返回给客户端。

配置 Client Details

The ClientDetailsServiceConfigurer (a callback from your AuthorizationServerConfigurer) can be used to define an in-memory or JDBC implementation of the client details service.

ClientDetailsServiceConfigurer可用使用client details service的两种实现中的任意一种:in-memory 或者 JDBC 

客户端重要的属性是:

  • clientId  :(必须的)客户端ID
  • secret  :(对于信任的客户端须要)客户端秘钥
  • scope  :客户端被限定的范围。若是scope为定义或者为空(默认为空)则客户端不受scope限制
  • authorizedGrantTypes  :客户端使用到的受权类型
  • authorities  :授予客户端的权限

客户端details能够在应用运行时被更新,经过直接访问存储(例如:若是用JdbcClientDetailsService的话能够实时改变数据库表中的数据)或者经过实现ClientDetailsManager接口(它们也都实现了ClientDetailsService接口)。

NOTE: the schema for the JDBC service is not packaged with the library (because there are too many variations you might like to use in practice), but there is an example you can start from in the test code in github.

注意:用于JDBC服务的数据库schema并无打包到library中(由于你再实际使用的时候可能有诸多差别),可是这里有一个例子你能够参考一下。

https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

 1 -- used in tests that use HSQL
 2 create table oauth_client_details (
 3     client_id VARCHAR(256) PRIMARY KEY,
 4     resource_ids VARCHAR(256),
 5     client_secret VARCHAR(256),
 6     scope VARCHAR(256),
 7     authorized_grant_types VARCHAR(256),
 8     web_server_redirect_uri VARCHAR(256),
 9     authorities VARCHAR(256),
10     access_token_validity INTEGER,
11     refresh_token_validity INTEGER,
12     additional_information VARCHAR(4096),
13     autoapprove VARCHAR(256)
14 );
15 
16 create table oauth_client_token (
17     token_id VARCHAR(256),
18     token LONGVARBINARY,
19     authentication_id VARCHAR(256) PRIMARY KEY,
20     user_name VARCHAR(256),
21     client_id VARCHAR(256)
22 );
23 
24 create table oauth_access_token (
25     token_id VARCHAR(256),
26     token LONGVARBINARY,
27     authentication_id VARCHAR(256) PRIMARY KEY,
28     user_name VARCHAR(256),
29     client_id VARCHAR(256),
30     authentication LONGVARBINARY,
31     refresh_token VARCHAR(256)
32 );
33 
34 create table oauth_refresh_token (
35     token_id VARCHAR(256),
36     token LONGVARBINARY,
37     authentication LONGVARBINARY
38 );
39 
40 create table oauth_code (
41     code VARCHAR(256), authentication LONGVARBINARY
42 );
43 
44 create table oauth_approvals (
45     userId VARCHAR(256),
46     clientId VARCHAR(256),
47     scope VARCHAR(256),
48     status VARCHAR(10),
49     expiresAt TIMESTAMP,
50     lastModifiedAt TIMESTAMP
51 );
52 
53 
54 -- customized oauth_client_details table
55 create table ClientDetails (
56     appId VARCHAR(256) PRIMARY KEY,
57     resourceIds VARCHAR(256),
58     appSecret VARCHAR(256),
59     scope VARCHAR(256),
60     grantTypes VARCHAR(256),
61     redirectUrl VARCHAR(256),
62     authorities VARCHAR(256),
63     access_token_validity INTEGER,
64     refresh_token_validity INTEGER,
65     additionalInformation VARCHAR(4096),
66     autoApproveScopes VARCHAR(256)
67 );

管理Tokens

AuthorizationServerTokenServices定义了管理OAuth 2.0 Token所必须的操做。请注意:

  • 当建立一个access token的时候,这个认证必须被存储起来,以便后续访问资源的时候对接收到的access token进行引用校验。
  • access token用来加载认证

当你实现了AuthorizationServerTokenServices接口,你可能考虑用DefaultTokenServices。有许多内置的插件化的策略能够用来改变access token的格式和存储。

默认状况下,用随机值来生成token,而且用TokenService来处理全部(除了token持久化之外)事情。默认的存储是in-memory实现,可是有其它的实现可使用。

  • 对于单服务器而言,默认的InMemoryTokenStore是完美的。大多数的项目是从这里开始的,为了使它很容易启动,也不须要其它依赖,而且可能以开发模式进行操做。
  • JdbcTokenStore是JDBC版本的Token存储。它把Token数据存储到关系型数据库中。为了使用JdbcTokenStore须要classpath下有"spring-jdbc"。
  • JSON Web Token (JWT) 它将受权的token的全部数据进行编码后存储(没有使用后端存储是它最大的优点)。这种方式的一个缺点是你不能很容易的撤销一个access token,所以通常用该方式存储的token的有效期很短,而且在刷新token的时候以前的token会被废除。另外一个缺点是,token很长,由于它里面存了不少关于用户凭证的信息。JwtTokenStore不会真的存储数据,它不持久化任何数据。可是在DefaultTokenServices中,它扮演着token值和认证信息转换的角色。

注意:对于JDBC的schema没有打包到library中,可是这儿有一个例子你能够参考一下test code in github。确保用@EnableTransactionManagement来防止多个客户端在同一行建立token。注意,示例中的schema都有明确地主键声明,在并发环境中这是必须的。

JWT Tokens

为了使用JWT Tokens,你须要在你的受权服务器中有一个JwtTokenStore。资源服务器也须要解码这个token,因此JwtTokenStore有一个依赖JwtAccessTokenConverter,相同的实现须要被包含在受权服务器和资源服务器中。也就是说,受权服务器和资源服务器中都须要JwtTokenStore实现。默认状况下,token是被签名的,并且资源服务器必须可以校验这个签名,所以须要有相同的对称key,或者须要公钥来匹配受权服务器上的私钥。公钥被受权服务器暴露在/oauth/token_key端点,默认状况下这个端点的访问规则是"denyAll()"。你能够用标准的SpEL表达式(例如:permitAll())到AuthorizationServerSecurityConfigurer来开放它。

为了使用JwtTokenStore,在classpath下须要有"spring-security-jwt"

Grant Types

受权类型经过AuthorizationEndpoint来支持。默认状况下,除了password之外,全部受权类型都支持。下面是受权类型的一些属性:

  • authenticationManager  :经过注入一个AuthenticationManager来切换成password受权
  • userDetailsService  :若是你注入一个UserDetailsService或者以任意方式配置了一个全局的UserDetailsService(例如:在GlobalAuthenticationManagerConfigurer中),那么一个刷新token将被包含在user detail中,为了强制帐户是激活的。
  • authorizationCodeServices  :定义受权码服务(AuthorizationCodeServices的实例)
  • implicitGrantService  :在隐式受权期间管理状态
  • tokenGranter  :tokenGranter

Configuring the Endpoint URLs

AuthorizationServerEndpointsConfigurer有一个pathMapping()方法。它有两个参数:

  • 端点的默认URL路径
  • 自定义的路径(必须以"/"开头)

下面是框架提供的URL路径:

  • /oauth/authorize    受权端点
  • /oauth/token    令牌端点
  • /oauth/confirm_access    用户批准受权的端点
  • /oauth/error    用于渲染受权服务器的错误
  • /oauth/check_token    资源服务器解码access token
  • /oauth/check_token    当使用JWT的时候,暴露公钥的端点

受权端点/oauth/authorize应该被保护起来,以致于它只能被认证过的用户访问。下面是一个例子,用标准的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资源。经过访问令牌来保护这些请求,你须要它们的路径不能与主用户过滤器链中的那些相匹配,因此请确保包含一个请求matcher,它只挑选出上面的WebSecurityConfigurer中的非api资源。

Customizing the UI

受权服务器的大多数端点主要都是被机器使用的,可是有两个资源是须要UI,它们分别是/oauth/confirm_access和HTML响应/oauth/error。框架为它们提供的实现是空白页,真实的状况是大多数受权服务器可能想要提供它们本身的实现来控制样式和内容。因此,你须要作的事情就是提供一个Spring MVC 被标注了@RequestMappings注解的Controller来映射这些端点,而且框架将用一个低的优先级来发放请求。在默认的/oauth/confirm_access你指望一个AuthorizationRequest绑定到session。你能够抓取请求的全部数据并按照本身喜欢的方式渲染它们,而后用户须要作的就是向/oauth/authorize发送关于批准或拒绝授予的信息。默认的UserApprovalHandler取决因而否你再AuthorizationServerEndpointsConfigurer中提供了一个ApprovalStore。标准的审批处理器以下:

  • TokenStoreUserApprovalHandler  :经过user_oauth_approval作一个简单的yes/no决定等同于“true”或“false”
  • ApprovalStoreUserApprovalHandler  :一组"scope*"参数key。参数的值能够是"true"或者"approval"。至少有一个scope是approval才算是受权成功。(A grant is successful if at least one scope is approved.)

强制SSL

纯HTTP对于测试来讲是能够的,可是在生成中受权服务器应该使用SSL。你能够在一个安全的容器或代理后面运行应用程序,若是你正确地设置代理和容器(这与OAuth2无关),那么它应该能够正常工做。对于/authorize端点你须要把它看成正常的应用安全的一部分来作,对于/token端点在AuthorizationServerEndpointsConfigurer中有一个标记能够设置,经过用sslOnly()方法。

自定义错误处理

受权服务器用标准的Spring MVC特性来进行错误处理。

你能够提供本身的实现,经过添加@Controller而且带上@RequestMapping("/oauth/error")

Mapping User Roles to Scopes

有时候,为了限制token的scope,不只仅要根据指定的客户端的范围,也要根据用户本身的权限来进行限制。若是你在你的AuthorizationEndpointDefaultOAuth2RequestFactory,你能够设置checkUserScopes=true来限制匹配的用户角色的容许范围。AuthorizationServerEndpointsConfigurer容许你注入一个自定义的OAuth2RequestFactory

资源服务器配置

一个资源服务器(可能与受权服务器是相同的应用,也可能与受权服务器是分开的应用)经过OAuth2 Token服务受保护的资源。

Spring OAuth 提供一个Spring Security认证过滤器来实现这个保护。你能够

你能够在一个@Configuration类上用@EnableResourceServer来切换它,而且用ResourceServerConfigurer配置它。下列特性能够被配置:

  • tokenServices  :一个ResourceServerTokenServices的实例
  • resourceId  :资源ID(推荐的,若是存在的话会被受权服务器校验)
  • 资源服务器的其它扩展端点
  • request matchers for protected resources (defaults to all)
  • access rules for protected resources (defaults to plain "authenticated")
  • 其它经过HttpSecurity配置的自定义的受保护的资源

@EnableResourceServer注释将自动添加一个OAuth2AuthenticationProcessingFilter类型的过滤器到Spring安全过滤器链中。

OAuth 2.0 客户端

受保护的资源配置


 

受保护的资源(或者叫远程资源)能够用OAuth2ProtectedResourceDetails类型的bean来定义。一个被保护的资源由下列属性:

  • id :资源的id。这个id只是用于客户端查找资源。
  • clientId :OAuth Client id。
  • clientSecret :关联的资源的secret。默认非空
  • accessTokenUri :提供access_token的端点的uri
  • scope :逗号分隔的字符串,表明访问资源的范围。默认为空
  • clientAuthenticationScheme :客户端认证所采用的schema。建议的值:"http_basic"和"form"。默认是"http_basic"。

不一样的受权类型有不一样的OAuth2ProtectedResourceDetails的具体实现(例如:ClientCredentialsResourceDetails是"client_credentials"类型的具体实现)

  • userAuthorizationUri :用户受权uri,非必需的。

客户端配置


 

对于OAuth 2.0客户端配置,简化的配置用@EnableOAuth2Client。这个注解作两件事情:

  • 建立一个过滤器(ID是oauth2ClientContextFilter)来存储当前的请求和上下文。在请求期间须要进行身份认证时,它管理重定向URI。
  • 在请求范围内建立一个AccessTokenRequest类型的bean。对于受权代码(或隐式)授予客户端是颇有用的,能够避免与单个用户相关的状态发生冲突。

AccessTokenRequest能够用在一个OAuth2RestTemplate,就像下面这样:

@Autowired
private OAuth2ClientContext oauth2Context;

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

访问受保护的资源


 

建议用RestTemplate访问受保护的资源。

Spring Security为OAuth提供了一个扩展的RestTemplate只须要你提供一个OAuth2ProtectedResourceDetails的实例便可。为了使它和用户token(受权码方式受权)一块儿使用,你应该考虑用@EnableOAuth2Client配置。

通常来讲,web应用程序不该该使用密码授予,所以若是您能够支持AuthorizationCodeResourceDetails,请避免使用ResourceOwnerPasswordResourceDetails

为了和用户令牌(受权码)一块儿使用,你应该考虑用@EnableOAuth2Client配置。

客户端持久化Token


 

客户端不须要持久化令牌,可是最好不要在每次重启客户端应用程序时都要求用户批准新的令牌授予。

ClientTokenServices接口定义了为特定用户保存OAuth 2.0令牌所需的操做。这是一个JDBC实现,可是若是您但愿实现本身的服务,以便在持久数据库中存储访问令牌和相关的身份验证明例,则能够这样作。若是你想要使用这个特性,你须要为OAuth2RestTemplate提供一个通过特殊配置的TokenProvider。例如:

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

 

参考

https://projects.spring.io/spring-security-oauth/docs/oauth2.html

相关文章
相关标签/搜索