zoukankan      html  css  js  c++  java
  • Spring Security Oauth2 的配置

    使用oauth2保护你的应用,可以分为简易的分为三个步骤

    • 配置资源服务器
    • 配置认证服务器
    • 配置spring security

    前两点是oauth2的主体内容,但前面我已经描述过了,spring security oauth2是建立在spring security基础之上的,所以有一些体系是公用的。

    oauth2根据使用场景不同,分成了4种模式

    • 授权码模式(authorization code)
    • 简化模式(implicit)
    • 密码模式(resource owner password credentials)
    • 客户端模式(client credentials)

    本文重点讲解接口对接中常使用的密码模式(以下简称password模式)和客户端模式(以下简称client模式)。授权码模式使用到了回调地址,是最为复杂的方式,通常网站中经常出现的微博,qq第三方登录,都会采用这个形式。简化模式不常用。

    配置资源服务器和授权服务器

    由于是两个oauth2的核心配置,我们放到一个配置类中。 
    为了方便下载代码直接运行,我这里将客户端信息放到了内存中,生产中可以配置到数据库中。token的存储一般选择使用redis,一是性能比较好,二是自动过期的机制,符合token的特性。

     1 @Configuration
     2 public class OAuth2ServerConfig {
     3 
     4     private static final String RESOURCE_ID = "wymlib";
     5     //资源配置服务器
     6     @Configuration
     7     @EnableResourceServer
     8     @Order(110)
     9     protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    10 
    11         @Override
    12         public void configure(ResourceServerSecurityConfigurer resources) {
    13             resources.resourceId(RESOURCE_ID).stateless(true);
    14         }
    15 
    16         @Override
    17         public void configure(HttpSecurity http) throws Exception {
    18             http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
    19                 .and()
    20                 .requestMatchers().anyRequest()
    21                 .and()
    22                 .anonymous()
    23                 .and()
    24                 .authorizeRequests()
    25                 .antMatchers("/api/v1/**").authenticated();//配置访问控制,必须认证过后才可以访问
    26         }
    27     }
    28 
    29     //认证服务器
    30     @Configuration
    31     @EnableAuthorizationServer
    32     @Order(99)
    33     protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    34 
    35         @Autowired
    36         AuthenticationManager authenticationManager;
    37 //        @Autowired
    38 //        RedisConnectionFactory redisConnectionFactory;
    39         
    40         @Value("${config.oauth2.clientID}")
    41         String clientID;
    42 
    43         @Value("${config.oauth2.clientSecret}")
    44         String clientSecret;
    45 
    46         @Value("${config.oauth2.accessTokenValiditySeconds}")
    47         int accessTokenValiditySeconds;
    48 
    49         @Override
    50         public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    51             clients.inMemory().withClient(clientID)
    52                     .resourceIds(RESOURCE_ID)
    53                     .authorizedGrantTypes("client_credentials", "refresh_token")
    54                     .scopes("select")
    55                     .authorities("client")
    56                     .secret(clientSecret)
    57                     .accessTokenValiditySeconds(accessTokenValiditySeconds);
    58         }
    59 
    60         /*@Override
    61         public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    62             endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
    63                     .authenticationManager(authenticationManager);
    64         }*/
    65 
    66         @Override
    67         public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
    68             //允许表单认证
    69             oauthServer.allowFormAuthenticationForClients();
    70         }
    71 
    72     }
    73 
    74 }

    简单说下spring security oauth2的认证思路。

    • client模式,没有用户的概念,直接与认证服务器交互,用配置中的客户端信息去申请accessToken,客户端有自己的client_id,client_secret对应于用户的username,password,而客户端也拥有自己的authorities,当采取client模式认证时,对应的权限也就是客户端自己的authorities。

    • password模式,自己本身有一套用户体系,在认证时需要带上自己的用户名和密码,以及客户端的client_id,client_secret。此时,accessToken所包含的权限是用户本身的权限,而不是客户端的权限。

    我对于两种模式的理解便是,如果你的系统已经有了一套用户体系,每个用户也有了一定的权限,可以采用password模式;如果仅仅是接口的对接,不考虑用户,则可以使用client模式。

    配置spring security

    在spring security的版本迭代中,产生了多种配置方式,建造者模式,适配器模式等等设计模式的使用,spring security内部的认证flow也是错综复杂,在我一开始学习ss也产生了不少困惑,总结了一下配置经验:使用了springboot之后,spring security其实是有不少自动配置的,我们可以仅仅修改自己需要的那一部分,并且遵循一个原则,直接覆盖最需要的那一部分。这一说法比较抽象,举个例子。比如配置内存中的用户认证器。有两种配置方式

     1 planA:
     2 
     3 @Bean
     4 protected UserDetailsService userDetailsService(){
     5     InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
     6     manager.createUser(User.withUsername("user_1").password("123456").authorities("USER").build());
     7     manager.createUser(User.withUsername("user_2").password("123456").authorities("USER").build());
     8     return manager;
     9 }
    10 planB:
    11 
    12 @Configuration
    13 @EnableWebSecurity
    14 public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    15 
    16     @Override
    17     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    18         auth.inMemoryAuthentication()
    19                 .withUser("user_1").password("123456").authorities("USER")
    20                 .and()
    21                 .withUser("user_2").password("123456").authorities("USER");
    22    }
    23 
    24    @Bean
    25    @Override
    26    public AuthenticationManager authenticationManagerBean() throws Exception {
    27        AuthenticationManager manager = super.authenticationManagerBean();
    28         return manager;
    29     }
    30 }

    你最终都能得到配置在内存中的两个用户,前者是直接替换掉了容器中的UserDetailsService,这么做比较直观;后者是替换了AuthenticationManager,当然你还会在SecurityConfiguration 复写其他配置,这么配置最终会由一个委托者去认证。如果你熟悉spring security,会知道AuthenticationManager和AuthenticationProvider以及UserDetailsService的关系,他们都是顶级的接口,实现类之间错综复杂的聚合关系…配置方式千差万别,但理解清楚认证流程,知道各个实现类对应的职责才是掌握spring security的关键。

    下面给出我最终的配置:

     1 @Configuration
     2 @EnableWebSecurity
     3 public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
     4 
     5     @Bean
     6     @Override
     7     protected UserDetailsService userDetailsService(){
     8         InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
     9         manager.createUser(User.withUsername("user_1").password("123456").authorities("USER").build());
    10         manager.createUser(User.withUsername("user_2").password("123456").authorities("USER").build());
    11         return manager;
    12     }
    13 
    14     @Override
    15     protected void configure(HttpSecurity http) throws Exception {
    16         // @formatter:off
    17         http
    18             .requestMatchers().anyRequest()
    19             .and()
    20                 .authorizeRequests()
    21                 .antMatchers("/oauth/*").permitAll();
    22         // @formatter:on
    23     }
    24 }

    重点就是配置了一个UserDetailsService,和ClientDetailsService一样,为了方便运行,使用内存中的用户,实际项目中,一般使用的是数据库保存用户,具体的实现类可以使用JdbcDaoImpl或者JdbcUserDetailsManager。

     

    xml配置:

      1 <!-- OAuth2 URL: /oauth/token 的处理与配置 一般使用时这里不需要修改, 直接使用即可 -->
      2     <sec:http pattern="/oauth/token" create-session="stateless"
      3         authentication-manager-ref="oauth2AuthenticationManager"
      4         entry-point-ref="oauth2AuthenticationEntryPoint" use-expressions="false">
      5         <sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
      6         <sec:anonymous enabled="false" />
      7         <sec:http-basic entry-point-ref="oauth2AuthenticationEntryPoint" />
      8         <sec:custom-filter ref="clientCredentialsTokenEndpointFilter"
      9             before="BASIC_AUTH_FILTER" />
     10         <sec:access-denied-handler ref="oauth2AccessDeniedHandler" />
     11         <!-- <csrf disabled="true"/> -->
     12     </sec:http>
     13     
     14     
     15     
     16     <sec:authentication-manager id="oauth2AuthenticationManager">
     17         <sec:authentication-provider
     18             user-service-ref="oauth2ClientDetailsUserService" />
     19     </sec:authentication-manager>
     20     
     21     
     22         <beans:bean id="oauth2ClientDetailsUserService"
     23         class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
     24         <beans:constructor-arg ref="clientDetailsService" />
     25         </beans:bean>
     26         
     27         <!-- 管理 ClientDetails -->
     28         <beans:bean id="clientDetailsService"
     29             class="org.springframework.security.oauth2.provider.client.JdbcClientDetailsService">
     30             <beans:constructor-arg index="0" ref="dataSource" />
     31         </beans:bean>
     32         
     33     <beans:bean id="oauth2AuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" />
     34         
     35     <!-- 处理grant_type=client_credentials 的逻辑 只从请求中获取client_id与client_secret -->
     36     <beans:bean id="clientCredentialsTokenEndpointFilter"
     37         class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
     38         <beans:property name="authenticationManager" ref="oauth2AuthenticationManager" />
     39     </beans:bean>
     40 
     41     
     42     
     43 
     44 拦截api/v1:
     45 
     46     <sec:http pattern="/api/v1/**" create-session="never"
     47         entry-point-ref="oauth2AuthenticationEntryPoint"
     48         access-decision-manager-ref="oauth2AccessDecisionManager"
     49         use-expressions="false">
     50         <sec:anonymous enabled="false" />
     51         <sec:intercept-url pattern="/api/v1/**"
     52             access="IS_AUTHENTICATED_ANONYMOUSLY" />
     53         <sec:custom-filter ref="unityResourceServer" before="PRE_AUTH_FILTER" />
     54         <sec:access-denied-handler ref="oauth2AccessDeniedHandler" />
     55     </sec:http>
     56     
     57     <!-- 扩展Spring Security 默认的 AccessDecisionManager 添加对OAuth中 scope 的检查与校验 -->
     58     <beans:bean id="oauth2AccessDecisionManager"
     59         class="org.springframework.security.access.vote.UnanimousBased">
     60         <beans:constructor-arg>
     61             <beans:list>
     62                 <beans:bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
     63                 <beans:bean class="org.springframework.security.access.vote.RoleVoter" />
     64                 <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
     65             </beans:list>
     66         </beans:constructor-arg>
     67     </beans:bean>    
     68     
     69     <!-- 每一个资源(resource)的定义, resource-id必须唯一, OauthClientDetails中的resourceIds属性的值由此来的, 
     70         允许一个Client有多个resource-id, 由逗号(,)分隔 每一个定义会在Security Flow中添加一个位于 PRE_AUTH_FILTER 
     71         之前的Filter -->
     72     <!--unity resource server filter -->
     73     <oauth2:resource-server id="unityResourceServer"
     74         resource-id="unity-resource" token-services-ref="tokenServices" />
     75 
     76     <beans:bean id="tokenServices"
     77         class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
     78         <beans:property name="tokenStore" ref="tokenStore" />
     79         <beans:property name="clientDetailsService" ref="clientDetailsService" />
     80         <beans:property name="supportRefreshToken" value="true" />
     81         <beans:property name="accessTokenValiditySeconds"    value="30" />
     82     </beans:bean>
     83     
     84     <!--Config token services -->
     85     <!--<beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore"/> -->
     86     <beans:bean id="tokenStore"
     87         class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
     88         <beans:constructor-arg index="0" ref="dataSource" />
     89     </beans:bean>
     90 
     91 
     92 
     93 <!-- Security OAuth Flow的核心配置 每一个配置对应一类具体的grant_type 可根据需求删除或禁用, 如: <oauth2:implicit 
     94         disabled="true"/> 默认支持OAuth2提供的5类grant_type, 若不需要任何一类, 将其配置注释掉(或删掉)即可. 若需要自定义 
     95         authorization url, 在 <oauth2:authorization-server > 配置中添加authorization-endpoint-url,如: 
     96         authorization-endpoint-url="/oauth2/authorization" 若需要自定义 token url, 在 <oauth2:authorization-server 
     97         > 配置中添加token-endpoint-url配置, 如:token-endpoint-url="/oauth2/my_token" -->
     98     <oauth2:authorization-server
     99         client-details-service-ref="clientDetailsService" token-services-ref="tokenServices"
    100         user-approval-handler-ref="oauthUserApprovalHandler"
    101         user-approval-page="oauth_approval" error-page="oauth_error">
    102         <oauth2:authorization-code authorization-code-services-ref="jdbcAuthorizationCodeServices" />
    103         <oauth2:implicit />
    104         <oauth2:refresh-token />
    105         <oauth2:client-credentials />
    106         <oauth2:password />
    107     </oauth2:authorization-server>
    108 
    109     
    110     <beans:bean id="oauthUserApprovalHandler"
    111         class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
    112         <beans:property name="tokenStore" ref="tokenStore" />
    113         <beans:property name="clientDetailsService" ref="clientDetailsService" />
    114         <beans:property name="requestFactory" ref="oAuth2RequestFactory" />
    115         <!-- <beans:property name="oauthService" ref="oauthService"/> -->
    116     </beans:bean>
    117 
    118     <!-- 管理 Authorization code -->
    119     <beans:bean id="jdbcAuthorizationCodeServices"
    120         class="org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices">
    121         <beans:constructor-arg index="0" ref="dataSource" />
    122     </beans:bean>
    123 
    124     
    125     
    126 请求token的url:
    127 http://192.9.8.144/ymlib/oauth/token?client_id=aa086e67c36342ed9ab6519247f5b68b&client_secret=mQDUMa03Rdy5vcMWjYHJmitkWJi3Rosr&grant_type=client_credentials

    参考资料:

    从零开始的Spring Security Oauth2(一)

    从零开始的Spring Security Oauth2(二)

    从零开始的Spring Security Oauth2(三)

  • 相关阅读:
    HDU 1058 Humble Numbers
    HDU 1421 搬寝室
    HDU 1176 免费馅饼
    七种排序算法的实现和总结
    算法纲要
    UVa401 回文词
    UVa 10361 Automatic Poetry
    UVa 537 Artificial Intelligence?
    UVa 409 Excuses, Excuses!
    UVa 10878 Decode the tape
  • 原文地址:https://www.cnblogs.com/hoojjack/p/8485478.html
Copyright © 2011-2022 走看看