当前位置:   article > 正文

搭建spring-security-oauth2授权服务(服务)和资源服务(模块)(一)—— 搭建授权服务_the class with org.springframework.security.oauth2

the class with org.springframework.security.oauth2.provider.oauth2authentica

之前用的是springsecurity+jwt作为安全验证,现在oauth2版本也可以实现,而且还有对外的token获取方式,所以替换成oauth2版本的springsecurity。

同样是使用jwt管理token,但是会加上redis,因为jwt生成的token是无状态的,所以生成新toekn后,旧token依旧能用,所以使用redis作为中间件来避免旧token还能使用的情况,实现单点登陆。

项目接口,红框里的是没用的,不用管

首先创建一个springboot项目,由于我用的是微服务,授权服务只是其中一个服务,所以可能会存在依赖缺少的情况,如果缺少依赖,评论留言。

然后是pom,pom中添加了项目中的其他模块,后面我会把源码上传至git

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-security</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.security.oauth.boot</groupId>
  12. <artifactId>spring-security-oauth2-autoconfigure</artifactId>
  13. <version>2.1.0.RELEASE</version>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.springframework.boot</groupId>
  17. <artifactId>spring-boot-starter-test</artifactId>
  18. <scope>test</scope>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  23. </dependency>
  24. <!--自定义组件-->
  25. <dependency>
  26. <groupId>com.rmt</groupId>
  27. <artifactId>rmt-db-jpa</artifactId>
  28. <version>0.0.1-SNAPSHOT</version>
  29. </dependency>
  30. <dependency>
  31. <groupId>com.rmt</groupId>
  32. <artifactId>rmt-domain-authority</artifactId>
  33. <version>0.0.1-SNAPSHOT</version>
  34. </dependency>
  35. <dependency>
  36. <groupId>com.rmt</groupId>
  37. <artifactId>rmt-redis</artifactId>
  38. <version>0.0.1-SNAPSHOT</version>
  39. </dependency>
  40. <dependency>
  41. <groupId>com.rmt</groupId>
  42. <artifactId>rmt-common-region</artifactId>
  43. <version>0.0.1-SNAPSHOT</version>
  44. </dependency>
  45. </dependencies>

创建数据库security_oauth2

  1. /*
  2. Navicat Premium Data Transfer
  3. Source Server : localhost
  4. Source Server Type : MySQL
  5. Source Server Version : 50717
  6. Source Host : localhost:3306
  7. Source Schema : security_oauth2
  8. Target Server Type : MySQL
  9. Target Server Version : 50717
  10. File Encoding : 65001
  11. Date: 05/03/2021 10:48:56
  12. */
  13. SET NAMES utf8mb4;
  14. SET FOREIGN_KEY_CHECKS = 0;
  15. -- ----------------------------
  16. -- Table structure for menu
  17. -- ----------------------------
  18. DROP TABLE IF EXISTS `menu`;
  19. CREATE TABLE `menu` (
  20. `id` int(11) NOT NULL AUTO_INCREMENT,
  21. `menu_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单编号',
  22. `menu_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单名称',
  23. `router` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '路由',
  24. `imgsrc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图标地址',
  25. `index_num` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '序号',
  26. `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单类型(目录,菜单,按钮)',
  27. `perms` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限标识',
  28. `status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单状态(正常 ,停用)',
  29. PRIMARY KEY (`id`) USING BTREE,
  30. UNIQUE INDEX `MENU_UNIQUE`(`menu_code`) USING BTREE COMMENT 'menu唯一',
  31. UNIQUE INDEX `PERMS_UNIQUE`(`perms`) USING BTREE COMMENT 'perms唯一'
  32. ) ENGINE = InnoDB AUTO_INCREMENT = 171 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  33. -- ----------------------------
  34. -- Records of menu
  35. -- ----------------------------
  36. INSERT INTO `menu` VALUES (1, '1000', '总菜单', NULL, NULL, '1', '菜单', NULL, '正常');
  37. INSERT INTO `menu` VALUES (9, '10000001', '系统管理', '', 'el-icon-user-solid', '0', '目录', 'system', '正常');
  38. INSERT INTO `menu` VALUES (11, '100000010002', '用户管理', '/UserIndex', '', '1', '菜单', 'system:user', '正常');
  39. INSERT INTO `menu` VALUES (12, '100000010003', '角色权限', '/RoleIndex', '', '2', '菜单', 'system:role', '正常');
  40. INSERT INTO `menu` VALUES (18, '100000010005', '菜单管理', '/MenuIndex', '', '3', '菜单', 'system:menu', '正常');
  41. INSERT INTO `menu` VALUES (40, '100000010006', '所属单位管理', '/OrganizationIndex', '', '4', '菜单', 'system:company', '正常');
  42. INSERT INTO `menu` VALUES (44, '100000010007', '日志管理', '/LogIndex', NULL, '5', '菜单', 'system:logs', '正常');
  43. INSERT INTO `menu` VALUES (75, '1000000100020001', '添加用户', '', '', '0', '按钮', 'system:user:add', '正常');
  44. INSERT INTO `menu` VALUES (76, '1000000100050001', '添加菜单', '', '', '0', '按钮', 'system:menu:add', '正常');
  45. INSERT INTO `menu` VALUES (77, '1000000100050002', '菜单列表', '', '', '0', '按钮', 'system:menu:list', '正常');
  46. INSERT INTO `menu` VALUES (78, '1000000100050003', '菜单修改', '', '', '0', '按钮', 'system:menu:update', '正常');
  47. INSERT INTO `menu` VALUES (79, '1000000100050004', '菜单删除', '', '', '0', '按钮', 'system:menu:delete', '正常');
  48. INSERT INTO `menu` VALUES (80, '1000000100020002', '用户修改', '', '', '0', '按钮', 'system:user:update', '正常');
  49. INSERT INTO `menu` VALUES (88, '1000000100020003', '用户删除', '', '', '0', '按钮', 'system:user:delete', '正常');
  50. INSERT INTO `menu` VALUES (89, '1000000100020004', '用户查询', '', '', '0', '按钮', 'system:user:select', '正常');
  51. INSERT INTO `menu` VALUES (90, '1000000100030001', '添加角色', '', '', '0', '按钮', 'system:role:add', '正常');
  52. INSERT INTO `menu` VALUES (91, '1000000100030002', '角色修改', '', '', '0', '按钮', 'system:role:update', '正常');
  53. INSERT INTO `menu` VALUES (92, '1000000100030003', '角色删除', '', '', '0', '按钮', 'system:role:delete', '正常');
  54. INSERT INTO `menu` VALUES (93, '1000000100030004', '菜单配置', '', '', '0', '按钮', 'system:role:config', '正常');
  55. INSERT INTO `menu` VALUES (94, '1000000100060001', '添加单位', '', '', '0', '按钮', 'system:company:add', '正常');
  56. INSERT INTO `menu` VALUES (95, '1000000100060002', '单位修改', '', '', '0', '按钮', 'system:company:update', '正常');
  57. INSERT INTO `menu` VALUES (96, '1000000100060003', '单位删除', '', '', '0', '按钮', 'system:company:delete', '正常');
  58. INSERT INTO `menu` VALUES (101, '1000000100070001', '日志查询', '', '', '0', '按钮', 'system:logs:select', '正常');
  59. INSERT INTO `menu` VALUES (112, '1000000100060004', '单位查询', '', '', '0', '按钮', 'system:company:select', '正常');
  60. INSERT INTO `menu` VALUES (135, '1000000100020005', '用户列表', '', '', '0', '按钮', 'system:user:list', '正常');
  61. INSERT INTO `menu` VALUES (137, '1000000100030005', '角色列表', '', '', '0', '按钮', 'system:role:list', '正常');
  62. INSERT INTO `menu` VALUES (143, '1000000100060005', '所属单位管理列表', '', '', '0', '按钮', 'system:company:list', '正常');
  63. INSERT INTO `menu` VALUES (146, '1000000100070002', '日志管理列表', '', '', '0', '按钮', 'system:logs:list', '正常');
  64. -- ----------------------------
  65. -- Table structure for oauth_client_details
  66. -- ----------------------------
  67. DROP TABLE IF EXISTS `oauth_client_details`;
  68. CREATE TABLE `oauth_client_details` (
  69. `client_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '客户端的id(用于唯一标识每一个客户端(client);注册时必须填写(也可以服务端自动生成),这个字段是必须的,实际应用也有叫app_key)',
  70. `resource_ids` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '资源服务器的id,多个用,(逗号)隔开{客户端能访问的资源id集合,注册客户端时,根据实际需要可选择资源id,也可以根据不同的额注册流程,赋予对应的额资源id}',
  71. `client_secret` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客户端的秘钥{注册填写或者服务端自动生成,实际应用也有叫app_secret, 必须要有前缀代表加密方式}',
  72. `scope` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '指定客户端申请的权限范围,可选值包括read,write,trust;若有多个权限范围用逗号(,)分隔,如: \"read,write\".@EnableGlobalMethodSecurity(prePostEnabled = true)启用方法级权限控制然后在方法上注解标识@PreAuthorize(\"#oauth2.hasScope(\'read\')\")',
  73. `authorized_grant_types` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '认证的方式{可选值 授权码模式:authorization_code,密码模式:password,刷新token: refresh_token, 隐式模式: implicit: 客户端模式: client_credentials。支持多个用逗号分隔}',
  74. `web_server_redirect_uri` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '授权码模式认证成功跳转的地址{客户端重定向uri,authorization_code和implicit需要该值进行校验,注册时填写,}',
  75. `authorities` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '指定用户的权限范围,如果授权的过程需要用户登陆,该字段不生效,implicit和client_credentials需要',
  76. `access_token_validity` int(11) NULL DEFAULT NULL COMMENT 'token的过期时间{设置access_token的有效时间(秒),默认(60 * 60 * 12,12小时)}',
  77. `refresh_token_validity` int(11) NULL DEFAULT NULL COMMENT '刷新token的过期时间{设置refresh_token有效期(秒),默认(60 *60 * 24 * 30, 30天)}',
  78. `additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '值必须是json格式',
  79. `autoapprove` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '默认false,适用于authorization_code模式,设置用户是否自动approval操作,设置true跳过用户确认授权操作页面,直接跳到redirect_uri',
  80. PRIMARY KEY (`client_id`) USING BTREE
  81. ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
  82. -- ----------------------------
  83. -- Records of oauth_client_details
  84. -- ----------------------------
  85. INSERT INTO `oauth_client_details` VALUES ('demo-client', NULL, '$2a$10$tj/PXVj9MBRdyuBKq99zeOw6oGkPVe7HNOxjmBWh.hsRmaU4IT2Ba', 'all', 'authorization_code,refresh_token,password', 'http://localhost:17772/home', NULL, 3600, 36000, NULL, '1');
  86. -- ----------------------------
  87. -- Table structure for organization
  88. -- ----------------------------
  89. DROP TABLE IF EXISTS `organization`;
  90. CREATE TABLE `organization` (
  91. `id` bigint(11) NOT NULL AUTO_INCREMENT,
  92. `organization_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '组织机构编号',
  93. `organization_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '组织机构名称',
  94. `savetime` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '时间',
  95. `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述',
  96. PRIMARY KEY (`id`) USING BTREE,
  97. UNIQUE INDEX `uk_ code`(`organization_code`) USING BTREE
  98. ) ENGINE = InnoDB AUTO_INCREMENT = 65 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  99. -- ----------------------------
  100. -- Records of organization
  101. -- ----------------------------
  102. INSERT INTO `organization` VALUES (1, '1000', '总组织', '1607496672156', '父集团');
  103. -- ----------------------------
  104. -- Table structure for role
  105. -- ----------------------------
  106. DROP TABLE IF EXISTS `role`;
  107. CREATE TABLE `role` (
  108. `id` bigint(20) NOT NULL AUTO_INCREMENT,
  109. `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  110. `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '描述',
  111. PRIMARY KEY (`id`) USING BTREE,
  112. UNIQUE INDEX `nameunique`(`name`) USING BTREE COMMENT 'role name唯一'
  113. ) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  114. -- ----------------------------
  115. -- Records of role
  116. -- ----------------------------
  117. INSERT INTO `role` VALUES (1, 'ROLE_ADMIN', '超级管理员');
  118. INSERT INTO `role` VALUES (2, 'ROLE_TEST', '测试用户');
  119. INSERT INTO `role` VALUES (6, 'ROLE_OR', '普通用户');
  120. -- ----------------------------
  121. -- Table structure for role_menu
  122. -- ----------------------------
  123. DROP TABLE IF EXISTS `role_menu`;
  124. CREATE TABLE `role_menu` (
  125. `id` int(11) NOT NULL AUTO_INCREMENT,
  126. `r_id` int(11) NULL DEFAULT NULL,
  127. `m_id` int(11) NULL DEFAULT NULL,
  128. PRIMARY KEY (`id`) USING BTREE
  129. ) ENGINE = InnoDB AUTO_INCREMENT = 6857 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  130. -- ----------------------------
  131. -- Records of role_menu
  132. -- ----------------------------
  133. INSERT INTO `role_menu` VALUES (6709, 1, 11);
  134. INSERT INTO `role_menu` VALUES (6710, 1, 75);
  135. INSERT INTO `role_menu` VALUES (6711, 1, 80);
  136. INSERT INTO `role_menu` VALUES (6712, 1, 88);
  137. INSERT INTO `role_menu` VALUES (6713, 1, 89);
  138. INSERT INTO `role_menu` VALUES (6714, 1, 135);
  139. -- ----------------------------
  140. -- Table structure for user
  141. -- ----------------------------
  142. DROP TABLE IF EXISTS `user`;
  143. CREATE TABLE `user` (
  144. `id` bigint(20) NOT NULL AUTO_INCREMENT,
  145. `iphone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  146. `organization_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  147. `organization_num` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  148. `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  149. `savetime` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  150. `sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  151. `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  152. `real_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '真实姓名',
  153. `user_type` enum('巡查员','管理员') CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '巡查员' COMMENT '用户类型',
  154. PRIMARY KEY (`id`) USING BTREE
  155. ) ENGINE = InnoDB AUTO_INCREMENT = 72 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  156. -- ----------------------------
  157. -- Records of user
  158. -- ----------------------------
  159. INSERT INTO `user` VALUES (1, '15227686967', '总组织', '1000', '$2a$10$7oxG1a5hefbal86RlTJnVOc5TXR6gcDCxWSP69H/mgAVL63wJ3F6S', '2020-05-28 13:42:15.000000', '2', 'admin', 'xiayanhui', '巡查员');
  160. -- ----------------------------
  161. -- Table structure for user_role
  162. -- ----------------------------
  163. DROP TABLE IF EXISTS `user_role`;
  164. CREATE TABLE `user_role` (
  165. `id` int(11) NOT NULL AUTO_INCREMENT,
  166. `u_id` int(11) NULL DEFAULT NULL,
  167. `r_id` int(11) NULL DEFAULT NULL,
  168. PRIMARY KEY (`id`) USING BTREE
  169. ) ENGINE = InnoDB AUTO_INCREMENT = 42 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  170. -- ----------------------------
  171. -- Records of user_role
  172. -- ----------------------------
  173. INSERT INTO `user_role` VALUES (1, 1, 1);
  174. INSERT INTO `user_role` VALUES (6, 13, 1);
  175. INSERT INTO `user_role` VALUES (19, 49, 8);
  176. INSERT INTO `user_role` VALUES (32, 62, 1);
  177. INSERT INTO `user_role` VALUES (33, 63, 6);
  178. INSERT INTO `user_role` VALUES (34, 64, 6);
  179. INSERT INTO `user_role` VALUES (35, 65, 1);
  180. INSERT INTO `user_role` VALUES (36, 66, 1);
  181. INSERT INTO `user_role` VALUES (37, 67, 1);
  182. INSERT INTO `user_role` VALUES (38, 68, 1);
  183. INSERT INTO `user_role` VALUES (39, 69, 1);
  184. INSERT INTO `user_role` VALUES (40, 70, 1);
  185. INSERT INTO `user_role` VALUES (41, 71, 1);
  186. SET FOREIGN_KEY_CHECKS = 1;

然后修改配置文件,bootstrap.yml

从配置文件应该可以看出,我使用的是nacos,所以nacos上也会存在一些配置文件。

  1. server:
  2. port: 17772
  3. ip-address: localhost
  4. namespace-id: 2120ba90-bbee-464c-bcff-62b039803d97
  5. spring:
  6. aop:
  7. auto: true
  8. application:
  9. name: oauth2-server
  10. cloud:
  11. nacos:
  12. discovery:
  13. #server-addr: 192.168.0.10:8848
  14. server-addr: ${ip-address}:8848
  15. #此处的namespace是discovery服务对应的命名空间,与config不同
  16. namespace: ${namespace-id}
  17. config:
  18. server-addr: ${ip-address}:8848
  19. file-extension: yaml
  20. #此处只是对应config的命名空间
  21. namespace: ${namespace-id}
  22. #共享配置文件
  23. shared-configs:
  24. - data-id: shared.yaml
  25. group: dev
  26. refresh: true
  27. - data-id: kafka-producer.yaml
  28. group: dev
  29. refresh: true
  30. - data-id: redis-config.yaml
  31. group: dev
  32. refresh: true
  33. #数据源
  34. datasource:
  35. name: db-base
  36. url: jdbc:mysql://${ip-address}:3306/security_oauth2?serverTimezone=GMT%2B8
  37. username: root
  38. password: root
  39. driver-class-name: com.mysql.cj.jdbc.Driver
  40. thymeleaf:
  41. prefix: classpath:/views/
  42. suffix: .html
  43. cache: false
  44. mvc:
  45. throw-exception-if-no-handler-found: true

然后开始配置授权服务

创建AuthServerConfig 里面注解很全

  1. package com.zz.zzoauth2.config;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.beans.factory.annotation.Qualifier;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.security.authentication.AuthenticationManager;
  6. import org.springframework.security.core.userdetails.UserDetailsService;
  7. import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
  8. import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
  9. import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
  10. import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
  11. import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
  12. import org.springframework.security.oauth2.provider.token.TokenEnhancer;
  13. import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
  14. import org.springframework.security.oauth2.provider.token.TokenStore;
  15. import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
  16. import javax.sql.DataSource;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. /**
  20. * OAuth2的授权服务:主要作用是OAuth2的客户端进行认证与授权
  21. *
  22. * @author wqy
  23. * @date 2020-09-04
  24. */
  25. @Configuration
  26. @EnableAuthorizationServer
  27. public class AuthServerConfig extends AuthorizationServerConfigurerAdapter{
  28. /**
  29. * 从数据库中查询账号密码
  30. */
  31. @Autowired
  32. @Qualifier("cusUserDetailsService")
  33. public UserDetailsService userDetailsService;
  34. /**
  35. * 从spring中获取数据源
  36. */
  37. @Autowired
  38. private DataSource dataSource;
  39. /**
  40. * 认证管理器
  41. */
  42. @Autowired
  43. private AuthenticationManager authenticationManager;
  44. /**
  45. * 它就是用来保存token
  46. */
  47. @Autowired
  48. private TokenStore jwtTokenStore;
  49. /**
  50. * TokenEnhancer的子类,帮助程序在JWT编码的令牌值和OAuth身份验证信息之间进行转换(在两个方向上),
  51. * 同时充当TokenEnhancer授予令牌的时间。
  52. * 自定义的JwtAccessTokenConverter(把自己设置的jwt签名加入accessTokenConverter中)
  53. */
  54. @Autowired
  55. private JwtAccessTokenConverter jwtAccessTokenConverter;
  56. /**
  57. * 在AuthorizationServerTokenServices 实现存储访问令牌之前增强访问令牌的策略。
  58. */
  59. @Autowired
  60. private TokenEnhancer jwtTokenEnhancer;
  61. /**
  62. * 配置OAuth2的客户端信息:clientId、client_secret、authorization_type、redirect_url等。
  63. * 实际保存在数据库中,建表语句在resource下data中
  64. *
  65. * 注:主要是与数据库互相同步
  66. * @param clients
  67. * @throws Exception
  68. */
  69. @Override
  70. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  71. clients.jdbc(dataSource);
  72. }
  73. /**
  74. * 1.增加jwt 增强模式
  75. * 2.调用userDetailsService实现UserDetailsService接口,对客户端信息进行认证与授权
  76. * @param endpoints
  77. * @throws Exception
  78. */
  79. @Override
  80. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  81. /**
  82. * jwt 增强模式
  83. * 对令牌的增强操作就在enhance方法中
  84. * 下面在配置类中,将TokenEnhancer和JwtAccessConverter加到一个enhancerChain中
  85. *
  86. * 通俗点讲它做了两件事:
  87. * 给JWT令牌中设置附加信息和jti:jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
  88. * 判断请求中是否有refreshToken,如果有,就重新设置refreshToken并加入附加信息
  89. */
  90. TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
  91. List<TokenEnhancer> enhancerList = new ArrayList<TokenEnhancer>();
  92. enhancerList.add(jwtTokenEnhancer);
  93. enhancerList.add(jwtAccessTokenConverter);
  94. //将自定义Enhancer加入EnhancerChain的delegates数组中
  95. enhancerChain.setTokenEnhancers(enhancerList);
  96. endpoints.tokenStore(jwtTokenStore)
  97. .userDetailsService(userDetailsService)
  98. /**
  99. * 支持 password 模式
  100. */
  101. .authenticationManager(authenticationManager)
  102. .tokenEnhancer(enhancerChain)
  103. .accessTokenConverter(jwtAccessTokenConverter);
  104. // 最后一个参数为替换之后授权页面的url
  105. endpoints.pathMapping("/oauth/confirm_access","/custom/confirm_access");
  106. }
  107. @Override
  108. public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
  109. security
  110. .tokenKeyAccess("permitAll()")
  111. .checkTokenAccess("isAuthenticated()")
  112. .allowFormAuthenticationForClients();
  113. }
  114. }

然后创建JwtTokenConfig,此处主要是对JWT进行的配置,设置密钥,在资源服务器要使用相同的密钥

  1. package com.zz.zzoauth2.config;
  2. import com.zz.constant.Oauth2Constant;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.security.oauth2.provider.token.TokenEnhancer;
  6. import org.springframework.security.oauth2.provider.token.TokenStore;
  7. import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
  8. import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
  9. import java.util.HashMap;
  10. /**
  11. * JwtTokenConfig配置类
  12. * 使用TokenStroe将进入JwtTokenStore
  13. * 注:Spring-Sceurity使用TokenEnhancer和JwtAccessConverter增强jwt令牌
  14. * @author wqy
  15. * @version 1.0
  16. * @date 2021/3/3 14:15
  17. */
  18. @Configuration
  19. public class JwtTokenConfig {
  20. @Bean
  21. public TokenStore jwtTokenStore(){
  22. return new JwtTokenStore(jwtAccessTokenConverter());
  23. }
  24. /**
  25. * JwtAccessTokenConverter:TokenEnhancer的子类,帮助程序在JWT编码的令牌值和OAuth身份验证信息之间进行转换(在两个方向上),同时充当TokenEnhancer授予令牌的时间。
  26. * 自定义的JwtAccessTokenConverter:把自己设置的jwt签名加入accessTokenConverter中(这里设置'demo',项目可将此在配置文件设置)
  27. * @return
  28. */
  29. @Bean
  30. public JwtAccessTokenConverter jwtAccessTokenConverter() {
  31. JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
  32. accessTokenConverter.setSigningKey(Oauth2Constant.JWT_SIGNING_KEY);
  33. return accessTokenConverter;
  34. }
  35. /**
  36. * 引入自定义JWTokenEnhancer:
  37. * 自定义JWTokenEnhancer实现TokenEnhancer并重写enhance方法,将附加信息加入oAuth2AccessToken中
  38. * @return
  39. */
  40. @Bean
  41. public TokenEnhancer jwtTokenEnhancer(){
  42. return new JwtTokenEnhancer();
  43. }
  44. }

然后创建JwtTokenEnhancer作为token增强,因为oauth2生成的token非常的短,而且不能存储信息,所以旧使用增强器,来存储更多的信息,同样生成的token也会更长。

TODO可以看一下,使用redis作为中间件,来实现旧token的伪失效

  1. package com.zz.zzoauth2.config;
  2. import com.zz.zzoauth2.bean.ContextBeans;
  3. import com.zz.zzoauth2.domain.AuthUser;
  4. import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
  5. import org.springframework.security.oauth2.common.OAuth2AccessToken;
  6. import org.springframework.security.oauth2.provider.OAuth2Authentication;
  7. import org.springframework.security.oauth2.provider.token.TokenEnhancer;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. import java.util.concurrent.TimeUnit;
  11. /**
  12. * TokenEnhancer:在AuthorizationServerTokenServices 实现存储访问令牌之前增强访问令牌的策略。
  13. * 自定义TokenEnhancer的代码:把附加信息加入oAuth2AccessToken中
  14. *
  15. * @author Tom
  16. * @date 2020-09-04
  17. */
  18. public class JwtTokenEnhancer implements TokenEnhancer {
  19. /**
  20. * 重写enhance方法,将附加信息加入oAuth2AccessToken中
  21. * @param oAuth2AccessToken
  22. * @param oAuth2Authentication
  23. * @return
  24. */
  25. @Override
  26. public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
  27. //TODO:token过期可以在此处想办法,结合redis,username可以做redis的key,value为jtl,
  28. // 在这生成一条以username为key的redis记录,
  29. // 资源服务根据解析出来的token,在redis中查找是否有相同的数据,有的话验证token中的jtl是否一致,
  30. // 不一致则提示token异常并删除redis中的该username,此处一定要保证redis中用户对应username的唯一性,
  31. // 避免大量并发击穿redis。
  32. // 单点登陆核心就是保证token的唯一性,现在保证jtl的唯一性就是保证token的唯一性
  33. // 步骤:
  34. // 1.获取jtl和到期时间
  35. // 2.以username为key并设置ttl(过期时间)
  36. // 3.由于系统里用户名是唯一的,所以存redis前要判断是否有重复的,有重复则删掉
  37. // 4.存入reids并设置ttl
  38. try {
  39. Map<String, Object> map = new HashMap<String, Object>();
  40. AuthUser authUser = (AuthUser) oAuth2Authentication.getPrincipal();
  41. authUser.getUser().setPassword(null);
  42. authUser.setPassword(null);
  43. map.put("authUser",authUser);
  44. ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(map);
  45. //是否启用redis
  46. if(ContextBeans.systemParam.getIsOpenOauth2Redis()){
  47. ContextBeans.redisTemplateUtils.set(authUser.getUsername(),oAuth2AccessToken.getValue(),oAuth2AccessToken.getExpiresIn(),TimeUnit.SECONDS);
  48. }
  49. }catch (Exception e){
  50. e.printStackTrace();
  51. }
  52. return oAuth2AccessToken;
  53. }
  54. }

创建SecurityConfig,此配置是关于security的配置,security的过滤器级别高于oauth2,所以要注意

  1. package com.zz.zzoauth2.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.security.authentication.AuthenticationManager;
  5. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  6. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  7. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  8. import org.springframework.security.crypto.password.PasswordEncoder;
  9. import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
  10. import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
  11. /**
  12. * @author wqy
  13. * @version 1.0
  14. * @date 2021/3/3 14:08
  15. */
  16. @Configuration
  17. @EnableAuthorizationServer
  18. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  19. /**
  20. * 引入密码加密类
  21. * @return
  22. */
  23. @Bean
  24. public PasswordEncoder passwordEncoder(){
  25. return new BCryptPasswordEncoder();
  26. }
  27. /**
  28. * 支持密码模式配置
  29. * @return
  30. * @throws Exception
  31. */
  32. @Override
  33. @Bean
  34. public AuthenticationManager authenticationManagerBean() throws Exception {
  35. return super.authenticationManagerBean();
  36. }
  37. /**
  38. * 配置URL访问授权,必须配置authorizeRequests(),否则启动报错,说是没有启用security技术。
  39. * 注意:在这里的身份进行认证与授权没有涉及到OAuth的技术:当访问要授权的URL时,请求会被DelegatingFilterProxy拦截,
  40. * 如果还没有授权,请求就会被重定向到登录界面。在登录成功(身份认证并授权)后,请求被重定向至之前访问的URL。
  41. * @param http
  42. * @throws Exception
  43. */
  44. @Override
  45. protected void configure(HttpSecurity http) throws Exception {
  46. http
  47. // 必须配置,不然OAuth2的http配置不生效----不明觉厉
  48. .requestMatchers()
  49. .antMatchers("/auth/login", "/auth/authorize","/oauth/authorize")
  50. .and()
  51. .authorizeRequests()
  52. // 自定义页面或处理url是,如果不配置全局允许,浏览器会提示服务器将页面转发多次
  53. .antMatchers("/auth/login", "/auth/authorize")
  54. .permitAll()
  55. .anyRequest()
  56. .authenticated();
  57. // 表单登录
  58. http.formLogin()
  59. // 登录页面
  60. .loginPage("/auth/login")
  61. // 登录处理url
  62. .loginProcessingUrl("/auth/authorize");
  63. http.httpBasic().disable();
  64. }
  65. }

然后CusUserDetailsService,连接数据库,登陆等等都是从此获取的数据

  1. package com.zz.zzoauth2.service;
  2. import com.zz.domain.authority.Organization;
  3. import com.zz.domain.authority.User;
  4. import com.zz.zzoauth2.domain.AuthUser;
  5. import com.zz.zzoauth2.resp.OrganizationJpa;
  6. import com.zz.zzoauth2.resp.RoleJpa;
  7. import com.zz.zzoauth2.resp.UserJpa;
  8. import org.springframework.security.core.userdetails.UserDetails;
  9. import org.springframework.security.core.userdetails.UserDetailsService;
  10. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  11. import org.springframework.stereotype.Component;
  12. /**
  13. * Spring-Security自定义身份认证类(实现UserDetailsService并重写loadUserByUsername方法)
  14. * 在loadUserByUsername方法内校验用户名密码是否正确并返回一个UserDetails对象
  15. *
  16. * @author Tom
  17. * @date 2020-09-04
  18. */
  19. @Component(value = "cusUserDetailsService")
  20. public class CusUserDetailsService implements UserDetailsService {
  21. private final UserJpa userJpa;
  22. private final RoleJpa roleJpa;
  23. private final OrganizationJpa organizationJpa;
  24. public CusUserDetailsService(UserJpa userJpa, RoleJpa roleJpa, OrganizationJpa organizationJpa) {
  25. this.userJpa = userJpa;
  26. this.roleJpa = roleJpa;
  27. this.organizationJpa = organizationJpa;
  28. }
  29. @Override
  30. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  31. User user = userJpa.findByUsername(username);
  32. Organization organization = organizationJpa.findNameByCode(user.getOrganizationNum());
  33. if (user == null) {
  34. throw new UsernameNotFoundException("user: " + username + " is not found.");
  35. }
  36. return new AuthUser(user.getUsername()
  37. , user.getPassword()
  38. , roleJpa.findByUserRole(user.getId())
  39. ,organization
  40. ,user);
  41. }
  42. }

上面就是一些配置代码和流程

配置实在是太多了,我就不一一配置, 看源码比较简单。

自定义授权和登陆页面我简单介绍一下,其实就是在配置AuthServerConfig 中配置授权页,因为授权是属于oauth2的,然后在SecurityConfig中配置登陆页,这两个分别对应的是Controller中的请求路由,

然后就可以跳转到对应的页面,源码中使用的是thymeleaf,页面在Resuorce下的views中。

配置security-oauth2主要还是要理解思路和过程,理解什么是授权,由于oauth2中有四种模式,源码可以使用鉴权码模式和密码模式,这两种也是最常用的。

源码中zz-security-oauth2-server下product.md中有使用介绍和简单介绍。

源码正在上传,下一章奉上

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/83406
推荐阅读
相关标签
  

闽ICP备14008679号