spring OAuth快速入门

如何阅读

前几节简单介绍了OAuth,以及spring secuirty OAuth的一些基本构成。文中有一些关联知识点的引用,若是要深刻研究,能够点进去详细了解一下,这样能够加深当即。若是你只想快速浏览OAuth server的搭建能够跳过前面几节,直接看下面的代码实例。html

什么是OAuth2

OAuth(开放受权)是一个开放标准,容许用户受权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不须要将用户名和密码提供给第三方移动应用或分享他们数据的全部内容。

Oauth2 受权方式java

  • authorization_code(受权码类型,使用三方qq登陆使用该方式)
  • implicit(隐式受权类型)
  • password(资源全部者即用户密码类型,典型应用是微服务里使用uaa服务器进行登陆认证)
  • client_credentials(客户端凭据【客户端ID以及Key】类型,典型应用是微服务内部系统与系统之间调用客服端受权)
  • refresh_token(经过以上受权得到的刷新令牌来获取新的令牌)。

关于OAuth不一样受权的具体流程能够参考[理解OAuth 2.0
-阮一峰](http://www.ruanyifeng.com/blo...算法

spring Security OAuth2

spring security OAuth 是对OAuth2协议的一个实现。是在spring security的基础上发展而来,以前是spring security的一个子项目,如今已经独立出来。详细见官网.
spring OAuth使用相似spring secuirty的机制来实现OAuth2 .根据OAuth协议规定,一个完整的受权服务器应当包含:受权服务器和资源服务器。受权服务器负责认证和受权,资源服务器负责根据用户提供的受权凭证(好比给以token)。这里有一篇官方文档介绍了spring OAuth的使用.[OAuth 2 Developers Guide
](http://projects.spring.io/spr...spring

受权服务

一个受权服务大体几个模块:client管理、受权接口、用户认证、令牌管理。
  1. client管理主要用来管理和区分不一样的client,咱们能够经过配置认证client连接是否合法,能为该client提供哪些受权服务,个性化定制client容许的行为。在spring secruity OAuth2中,能够对client进行以下属性配置:
  • clientId:(必须的)用来标识客户的Id。
  • secret:(须要值得信任的客户端)客户端安全码,若是有的话。
  • scope:用来限制客户端的访问范围,若是为空(默认)的话,那么客户端拥有所有的访问范围。
  • authorizedGrantTypes:此客户端可使用的受权类型,默认为空。
  • authorities:此客户端可使用的权限(基于Spring Security authorities)。
  1. 受权接口是受权服务对外提供的http入口。在spring中受权端口以下:

spring security OAuth授端口数据库

  • /oauth/authorize:受权端点。对应AuthorizationEndpoint类
  • /oauth/token:令牌端点。对应TokenEndpoint类
  • /oauth/confirm_access:用户确认受权提交端点。对应WhitelabelApprovalEndpoint类
  • /oauth/error:受权服务错误信息端点。对应WhitelabelApprovalEndpoint类
  • /oauth/check_token:用于资源服务访问的令牌解析端点。对应CheckTokenEndpoint类
  • /oauth/token_key:提供公有密匙的端点,若是你使用JWT令牌的话。对应TokenKeyEndpoint类

受权是使用 AuthorizationEndpoint 这个端点来进行控制的,你可以使用 AuthorizationServerEndpointsConfigurer 这个对象的实例来进行配置 ,若是你不进行设置的话,默认是除了资源全部者密码(password)受权类型之外,支持其他全部标准受权类型的(RFC6749),咱们来看一下这个配置对象有哪些属性能够设置吧,以下列表:后端

  • authenticationManager:认证管理器,当你选择了资源全部者密码(password)受权类型的时候,请设置这个属性注入一个 AuthenticationManager 对象。
  • userDetailsService:若是啊,你设置了这个属性的话,那说明你有一个本身的 UserDetailsService 接口的实现,或者你能够把这个东西设置到全局域上面去(例如 GlobalAuthenticationManagerConfigurer 这个配置对象),当你设置了这个以后,那么 "refresh_token" 即刷新令牌受权类型模式的流程中就会包含一个检查,用来确保这个帐号是否仍然有效,假如说你禁用了这个帐户的话。
  • authorizationCodeServices:这个属性是用来设置受权码服务的(即 AuthorizationCodeServices 的实例对象),主要用于 "authorization_code" 受权码类型模式。
  • implicitGrantService:这个属性用于设置隐式受权模式,用来管理隐式受权模式的状态。
  • tokenGranter:这个属性就很牛B了,当你设置了这个东西(即 TokenGranter 接口实现),那么受权将会交由你来彻底掌控,而且会忽略掉上面的这几个属性,这个属性通常是用做拓展用途的,即标准的四种受权模式已经知足不了你的需求的时候,才会考虑使用这个
  1. 用户认证,例如在使用password受权模式时,须要在获取令牌以前先校验用户提供的凭证是否合法,合法的凭证是用户获取受权令牌的前提,spring secuiurty OAuth使用了spring security的认证服务,在令牌获取端口AuthenticationManager进行受权,这个会在后面的受权端口中提到。
  2. 令牌管理服务负责令牌是生成、校验等操做。

令牌主要有两种解决方案:使用随机算法生成惟一标示与用户受权关联,而后保存起来供校验时查询,为了方便资源服务方便验证令牌,这种方案经常是受权服务和资源服务共存的,若是不共存,那么资源服务的tokenStore服务于受权服务tokenStore要作到数据互通,spring 的解决方案是提供/oauth/check_token接口来完成。第二种是受权服务器使用某种算法生成字符串,资源服务器使用约定好的算法对令牌进行解析校验,以验证 他的合法性。在spring security OAuth2中提供了InMemoryTokenStore、JdbcTokenStore、JwtTokenStore。前面二者须要将令牌存在起来,最后一个JwtTokenStore是jwt令牌TokenStore的实现,他不存储令牌,只根据必定的规则和秘钥验证令牌的合法性。jwt令牌分为三段:头部信息、playload、签名。每段使用base64编码,每段之间使用"."链接。api

资源服务

一个资源服务(能够和受权服务在同一个应用中,固然也能够分离开成为两个不一样的应用程序)提供一些受token令牌保护的资源,Spring OAuth提供者是经过Spring Security authentication filter来验证过滤器来实现的保护(OAuth2AuthenticationProcessingFilter),你能够经过 @EnableResourceServer 注解到一个 @Configuration 配置类上来标记应用是一个资源服务器,你能够经过配置 ResourceServerConfigurer 配置对象来进行资源服务器的一些自定义配置(能够选择继承自 ResourceServerConfigurerAdapter 而后覆写其中的方法,参数就是这个对象的实例),下面是一些能够配置的属性:缓存

  • tokenServices:ResourceServerTokenServices 类的实例,用来实现令牌服务。
  • resourceId:这个资源服务的ID,这个属性是可选的,可是推荐设置并在受权服务中进行验证。
  • 其余的拓展属性例如 tokenExtractor 令牌提取器用来提取请求中的令牌,也就说,你能够自定义提如何在请求中提取令牌。
  • 请求匹配器,用来设置须要进行保护的资源路径,默认的状况下是受保护资源服务的所有路径。
  • 受保护资源的访问规则,默认的规则是简单的身份验证(plain authenticated)。
  • 其余的自定义权限保护规则经过 HttpSecurity 来进行配置。

@EnableResourceServer 注解自动增长了一个类型为 OAuth2AuthenticationProcessingFilter 的过滤器链。
ResourceServerTokenServices 是组成受权服务的另外一半,若是你的受权服务和资源服务在同一个应用程序上的话,你可使用 DefaultTokenServices ,这样的话,你就不用考虑关于实现全部必要的接口的一致性问题,这一般是很困难的。若是你的资源服务器是分离开的,那么你就必需要确保可以有匹配受权服务提供的 ResourceServerTokenServices,它知道如何对令牌进行解码。
在受权服务器上,你一般可使用 DefaultTokenServices 而且选择一些主要的表达式经过 TokenStore(后端存储或者本地编码)。
RemoteTokenServices 能够做为一个替代,它将容许资源服务器经过HTTP请求来解码令牌(也就是受权服务的 /oauth/check_token 端点)。若是你的资源服务没有太大的访问量的话,那么使用RemoteTokenServices 将会很方便(全部受保护的资源请求都将请求一次受权服务用以检验token值),或者你能够经过缓存来保存每个token验证的结果。安全

spring Security OAuth 配置示例

关于spring OAuth的更多配置细节能够参考官方文档: OAuth2 Autoconfig.spring Secuirty OAuth2配置比较简单,关键仍是要理解OAuth2协议,以及他的使用场景。

添加依赖

<dependencies>
  <!-- ... other dependency elements ... -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.1.1.RELEASE</version>
  </dependency>
</dependencies>

受权服务配置

受权服务配置主要须要完成如下几个配置:bash

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

咱们能够继承AuthorizationServerConfigurerAdapter而且覆写其中的三个configure方法来进行配置。下面是简单的
配置demo,配置来源于官方文档。

@Component
public class CustomAuthorizationServerConfigurer extends
    AuthorizationServerConfigurerAdapter {

    AuthenticationManager authenticationManager;

    public CustomAuthorizationServerConfigurer(
        AuthenticationConfiguration authenticationConfiguration
    ) throws Exception {
        this.authenticationManager =
            authenticationConfiguration.getAuthenticationManager();
    }

    @Override
    public void configure(
        ClientDetailsServiceConfigurer clients
    ) throws Exception {
        //下面配置了一个简单的客服端
        clients.inMemory()// 使用内存保存客服端信息,建议编写本身的clientService来配置客服端
            .withClient("client")//客服端的clientid
                .authorizedGrantTypes("password","client_credentials","refresh_token")//支持的受权方式
                .secret("secret")//该客服端访问时的密码
                .scopes("all");//scope范围
    }

    @Override
    public void configure(
        AuthorizationServerEndpointsConfigurer endpoints
    ) throws Exception {
        endpoints.authenticationManager(authenticationManager);
        endpoints.tokenStore(tokenStore());
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }
    
}

以上完成了一个简单的受权服务配置,咱们的配置重点放在了client的配置上,因为咱们为配置资源拥有者(用户),咱们没法使用password受权,应为用户信不存在,咱们不管输入用户名和密码都不正确。所以我暂且只能使用,client_credentials,这个仅仅是client受权,不牵扯到用户信息。使用下面命令

curl client:secret@localhost:8080/oauth/token -d grant_type=client_credentials

一切顺利的话咱们能够获得token相关信息。若是要使用其余好比password等用户受权,那么须要在受权服务器配置用户相关信息,并把相关的userDetailService配置到AuthorizationServerEndpointsConfigurer对象上,上面有提到endponint配置相关规则。

资源服务配置

资源服务的做用前面已经介绍过了,资源服务主要关注两个事情,对token进行鉴权,根据具体权限包含资源。因为token多是其余服务器生成,所以要作到ResourceServerTokenServices与受权服务的tokenService相匹配。下面是简单的配置.

@EnableResourceServer
   public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

       // 注意这个TokenStore须要和以前配置的一致
       private final TokenStore tokenStore;


       public ResourceServerConfiguration(TokenStore tokenStore) {
           this.tokenStore = tokenStore;
       }

       // 资源服务器安全规则配置
       @Override
       public void configure(HttpSecurity http) throws Exception {
           http
                   .exceptionHandling()
                   .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
                   .and()
                   .csrf()
                   .disable()
                   .headers()
                   .frameOptions()
                   .disable()
                   .and()
                   .sessionManagement()
                   .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                   .and()
                   .authorizeRequests()
                   .antMatchers("/api/register").permitAll()
                   .antMatchers("/api/activate").permitAll()
                   .antMatchers("/api/authenticate").permitAll()
                   .antMatchers("/api/account/reset-password/init").permitAll()
                   .antMatchers("/api/account/reset-password/finish").permitAll()
                   .antMatchers("/api/**").authenticated()
                   // 配置scope权限访问
//                    .antMatchers("/api/**").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))");
       }

       @Override
       public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
           resources.resourceId("jhipster-uaa")
                   .tokenStore(tokenStore);
       }
   }

上面是资源服务和受权服务在一台机器上时的配置。因为共享tokenStore,所以资源服务在验证token的时候不会出任何问题。

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends ResourceServerConfigurerAdapter {
   private final OAuth2Properties oAuth2Properties;

   public SecurityConfiguration(OAuth2Properties oAuth2Properties) {
       this.oAuth2Properties = oAuth2Properties;
   }

   // 主要配置资源的保护规则
   @Override
   public void configure(HttpSecurity http) throws Exception {
       http
           .csrf()
           .disable()
           .headers()
           .frameOptions()
           .disable()
       .and()
           .sessionManagement()
           .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
       .and()
           .authorizeRequests()
           .antMatchers("/api/**").authenticated() 
           .antMatchers("/management/health").permitAll()
           .antMatchers("/management/info").permitAll()
           .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN);
   }

   // 配置tokenstore,资源服务器在进行token验证的时候须要,
   @Bean
   public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
       return new JwtTokenStore(jwtAccessTokenConverter);
   }

   @Bean
   public JwtAccessTokenConverter jwtAccessTokenConverter(OAuth2SignatureVerifierClient signatureVerifierClient) {
       return new OAuth2JwtAccessTokenConverter(oAuth2Properties, signatureVerifierClient);
   }

}

上面是采用jwt,资源/受权服务分离的配置,以上配置没法和上面的受权服务配置配合使用。若要配置使用,须要修改受权服务的TokenStore为JwtTokenStore,而且在OAuth2JwtAccessTokenConverter中需访问受权服务器/token/access_key获取RSA公钥,该公钥会在token验证时用到。

相关文章
相关标签/搜索