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

Spring Security Oauth2 的配置的更多相关文章

  1. Spring Security OAuth2 Demo -- good

    1. 添加依赖授权服务是基于Spring Security的,因此需要在项目中引入两个依赖: <dependency> <groupId>org.springframework ...

  2. Spring Security OAuth2 Demo

    Spring Security OAuth2 Demo 项目使用的是MySql存储, 需要先创建以下表结构: CREATE SCHEMA IF NOT EXISTS `alan-oauth` DEFA ...

  3. spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)

    最近再学习spring security oauth2.下载了官方的例子sparklr2和tonr2进行学习.但是例子里包含的东西太多,不知道最简单最主要的配置有哪些.所以决定自己尝试搭建简单版本的例 ...

  4. Spring Security OAuth2之resource_id配置与验证

    一.resource_id的作用 Spring Security OAuth2 架构上分为Authorization Server认证服务器和Resource Server资源服务器.我们可以为每一个 ...

  5. 【OAuth2.0】Spring Security OAuth2.0篇之初识

    不吐不快 因为项目需求开始接触OAuth2.0授权协议.断断续续接触了有两周左右的时间.不得不吐槽的,依然是自己的学习习惯问题,总是着急想了解一切,习惯性地钻牛角尖去理解小的细节,而不是从宏观上去掌握 ...

  6. 使用Spring Security Oauth2完成RESTful服务password认证的过程

            摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息.ClientDetails.token存入数据库而非内存.配置 ...

  7. Spring security oauth2最简单入门环境搭建

    关于OAuth2的一些简介,见我的上篇blog:http://wwwcomy.iteye.com/blog/2229889 PS:貌似内容太水直接被鹳狸猿干沉.. 友情提示 学习曲线:spring+s ...

  8. Spring Security Oauth2系列(一)

    前言: 关于oauth2,其实是一个规范,本文重点讲解spring对他进行的实现,如果你还不清楚授权服务器,资源服务器,认证授权等基础概念,可以移步理解OAuth 2.0 - 阮一峰,这是一篇对于oa ...

  9. Spring Security Oauth2 permitAll()方法小记

    黄鼠狼在养鸡场山崖边立了块碑,写道:"不勇敢地飞下去,你怎么知道自己原来是一只搏击长空的鹰?!" 从此以后 黄鼠狼每天都能在崖底吃到那些摔死的鸡! 前言 上周五有网友问道,在使用s ...

随机推荐

  1. Docker系列05—Docker 存储卷详解

    本文收录在容器技术学习系列文章总目录 1.存储卷介绍 1.1 背景 (1)docker 的 AFUS 分层文件系统 docker镜像由多个只读层叠加面成,启动容器时,docker会加载只读镜像层并在镜 ...

  2. springmvc 项目完整示例02 项目创建-eclipse创建动态web项目 配置文件 junit单元测试

    包结构 所需要的jar包直接拷贝到lib目录下 然后选定 build path 之后开始写项目代码 配置文件 ApplicationContext.xml <?xml version=" ...

  3. 强大的jupyter,python开发者的福音

    jupyter是一种交互式计算和开发环境的笔记,ipython命令行比原生的python命令行更加友好和高效,还可以运行web版的界面,支持多语言,输出图形.音频.视频等功能. 一.安装 pip3 i ...

  4. Spring拓展接口之FactoryBean,我们来看看其源码实现

    前言 开心一刻 那年去相亲,地点在饭店里,威特先上了两杯水,男方绅士的喝了一口,咧嘴咋舌轻放桌面,手抚额头闭眼一脸陶醉,白水硬是喝出了82年拉菲的感觉.如此有生活情调的幽默男人,果断拿下,相处后却发现 ...

  5. 痞子衡嵌入式:第一本Git命令教程(4)- 转移(add/rm/mv)

    今天是Git系列课程第四课,上一课我们在Git空间里做了一些文件改动并且知道了如何利用Git查看这些变动,今天痞子衡要讲的是将这些变动提交到Git本地仓库前的准备工作. Git仓库目录下的文件改动操作 ...

  6. BootStrap之 提示工具(Tooltip)插件

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  7. @Html.DropDownListFor 下拉框绑定(选择默认值)

    首先先构建绑定下拉框的数据源 private void GetSalesList() { var userList = _rmaExpressAppService.GetUserList(); Tem ...

  8. 如何去掉C#字符串中的所有空格(转载)

    如何去掉C#字符串中的所有空格 来源:https://www.cnblogs.com/donchen/p/8966059.html 字符串行数Trim()可以去掉字符串前后的空格,如:  C# Cod ...

  9. 2019年度SAP项目实践计划

    2019年度SAP项目实践计划 一晃2018年过去了,而新的一年说来就来了. 对于新的一年,笔者也难免有所畅想.早在上个月下旬就开始制定新年的诸多计划,比如写作计划,比如人工智能学习计划,比如新年度旅 ...

  10. SAP MM盘点流程里如何处理事务代码MI11 Recount过的盘点凭证?

    SAP MM盘点流程里如何处理事务代码MI11 Recount过的盘点凭证? 1, MI01 create a physical inventory document, 2, MI04 to inpu ...