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(三)

  • 相关阅读:
    mac控制台快捷键
    idc函数大全
    idc指令相关
    idc交叉引用
    ida脚本函数
    2025年全球网络安全市场规模将达到30万亿美元
    是黑客驱动着汽车网络安全市场的趋势?听听大佬们怎么说
    不修复!你的设备会被黑客远程控制,专家警告修补这个漏洞
    共抗疫情!科技人员积极抗击”冠状病毒“大流行
    警惕黑客威胁汽车,车联网如何保证安全?
  • 原文地址:https://www.cnblogs.com/hoojjack/p/8485478.html
Copyright © 2011-2022 走看看