Tim's Note

试问Coding应不好,却道:此心安处是吾乡

0%

Spring Security oAuth2 (三)

本篇文章来看看基于角色的权限控制 —— RBAC (Role-Based Access Control),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样就构造成了 『用户 - 角色 - 权限』的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般是多对多的关系。理解了 RBAC 之后,再通过 Spring Security oAuth2 来实现基于 RBAC 的自定义认证。

RBAC 概述

RBAC 认为授权实际上是 Who 、What 、How 三元组之间的关系,也就是 Who 对 What 进行 How 的操作,也就是『主体』对『客体』的操作。
Who:是权限的拥有者或主体(如:User、Role)。
What:是操作或对象(operation、object)。
How:具体的权限(Privilege,正向授权与负向授权)

在我们的 oAuth2 系统中,我们需要对系统的所有资源进行权限控制,系统中的资源包括:
1、静态资源(对象资源):功能操作、数据列
2、动态资源(数据资源):文章、相册、笔记

系统的目的就是对应用系统的所有对象资源和数据资源进行权限控制,比如:功能菜单、界面按钮、数据显示的列、各种行级数据进行权限的操控。

创建数据表

所以根据上面的定义,我们至少需要五张表:
① user 用户表、② role 角色表、③ permission 权限表、④ user_role 用户 - 角色表、⑤ role_permission 角色 - 权限表

对应的 SQL 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
CREATE TABLE `tb_permission`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`parent_id` bigint(20) DEFAULT NULL COMMENT ' 父权限 ',
`name` varchar(64) NOT NULL COMMENT ' 权限名称 ',
`enname` varchar(64) NOT NULL COMMENT ' 权限英文名称 ',
`url` varchar(255) NOT NULL COMMENT ' 授权路径 ',
`description` varchar(200) DEFAULT NULL COMMENT ' 备注 ',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 37
DEFAULT CHARSET = utf8 COMMENT =' 权限表 ';

CREATE TABLE `tb_role`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`parent_id` bigint(20) DEFAULT NULL COMMENT ' 父角色 ',
`name` varchar(64) NOT NULL COMMENT ' 角色名称 ',
`enname` varchar(64) NOT NULL COMMENT ' 角色英文名称 ',
`description` varchar(200) DEFAULT NULL COMMENT ' 备注 ',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 37
DEFAULT CHARSET = utf8 COMMENT =' 角色表 ';

CREATE TABLE `tb_role_permission`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) NOT NULL COMMENT ' 角色 ID',
`permission_id` bigint(20) NOT NULL COMMENT ' 权限 ID',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 37
DEFAULT CHARSET = utf8 COMMENT =' 角色权限表 ';

CREATE TABLE `tb_user`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT ' 用户名 ',
`password` varchar(64) NOT NULL COMMENT ' 密码,加密存储 ',
`phone` varchar(20) DEFAULT NULL COMMENT ' 注册手机号 ',
`email` varchar(50) DEFAULT NULL COMMENT ' 注册邮箱 ',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`) USING BTREE,
UNIQUE KEY `phone` (`phone`) USING BTREE,
UNIQUE KEY `email` (`email`) USING BTREE
) ENGINE = InnoDB
AUTO_INCREMENT = 37
DEFAULT CHARSET = utf8 COMMENT =' 用户表 ';

CREATE TABLE `tb_user_role`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT ' 用户 ID',
`role_id` bigint(20) NOT NULL COMMENT ' 角色 ID',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 37
DEFAULT CHARSET = utf8 COMMENT =' 用户角色表 ';

初始化表数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for ClientDetails
-- ----------------------------
DROP TABLE IF EXISTS `ClientDetails`;
CREATE TABLE `ClientDetails` (
`appId` varchar(256) NOT NULL,
`resourceIds` varchar(256) DEFAULT NULL,
`appSecret` varchar(256) DEFAULT NULL,
`scope` varchar(256) DEFAULT NULL,
`grantTypes` varchar(256) DEFAULT NULL,
`redirectUrl` varchar(256) DEFAULT NULL,
`authorities` varchar(256) DEFAULT NULL,
`access_token_validity` int DEFAULT NULL,
`refresh_token_validity` int DEFAULT NULL,
`additionalInformation` varchar(4096) DEFAULT NULL,
`autoApproveScopes` varchar(256) DEFAULT NULL,
PRIMARY KEY (`appId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of ClientDetails
-- ----------------------------
BEGIN;
COMMIT;

-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
`token_id` varchar(256) DEFAULT NULL,
`token` blob,
`authentication_id` varchar(256) NOT NULL,
`user_name` varchar(256) DEFAULT NULL,
`client_id` varchar(256) DEFAULT NULL,
`authentication` blob,
`refresh_token` varchar(256) DEFAULT NULL,
PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for oauth_approvals
-- ----------------------------
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals` (
`userId` varchar(256) DEFAULT NULL,
`clientId` varchar(256) DEFAULT NULL,
`scope` varchar(256) DEFAULT NULL,
`status` varchar(10) DEFAULT NULL,
`expiresAt` timestamp NULL DEFAULT NULL,
`lastModifiedAt` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of oauth_approvals
-- ----------------------------
BEGIN;
COMMIT;

-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(256) NOT NULL,
`resource_ids` varchar(256) DEFAULT NULL,
`client_secret` varchar(256) DEFAULT NULL,
`scope` varchar(256) DEFAULT NULL,
`authorized_grant_types` varchar(256) DEFAULT NULL,
`web_server_redirect_uri` varchar(256) DEFAULT NULL,
`authorities` varchar(256) DEFAULT NULL,
`access_token_validity` int DEFAULT NULL,
`refresh_token_validity` int DEFAULT NULL,
`additional_information` varchar(4096) DEFAULT NULL,
`autoapprove` varchar(256) DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
BEGIN;
INSERT INTO `oauth_client_details` VALUES ('client', NULL, '$2a$10$O0BSekA2psUwp5.KyrmPDOBrP60iiFtsl2vOrXyEpW3uPcW8Jd0xi', 'app', 'authorization_code', 'https://example.com', NULL, NULL, NULL, NULL, NULL);
INSERT INTO `oauth_client_details` VALUES ('tencent', NULL, '$2a$10$O0BSekA2psUwp5.KyrmPDOBrP60iiFtsl2vOrXyEpW3uPcW8Jd0xi', 'all_info', 'authorization_code', 'https://qq.com', NULL, NULL, NULL, NULL, NULL);
COMMIT;

-- ----------------------------
-- Table structure for oauth_client_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token` (
`token_id` varchar(256) DEFAULT NULL,
`token` blob,
`authentication_id` varchar(256) NOT NULL,
`user_name` varchar(256) DEFAULT NULL,
`client_id` varchar(256) DEFAULT NULL,
PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of oauth_client_token
-- ----------------------------
BEGIN;
COMMIT;

-- ----------------------------
-- Table structure for oauth_code
-- ----------------------------
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
`code` varchar(256) DEFAULT NULL,
`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of oauth_code
-- ----------------------------
BEGIN;
COMMIT;

-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(256) DEFAULT NULL,
`token` blob,
`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of oauth_refresh_token
-- ----------------------------
BEGIN;
COMMIT;

-- ----------------------------
-- Table structure for tb_permission
-- ----------------------------
DROP TABLE IF EXISTS `tb_permission`;
CREATE TABLE `tb_permission` (
`id` bigint NOT NULL AUTO_INCREMENT,
`parent_id` bigint DEFAULT NULL COMMENT ' 父权限 ',
`name` varchar(64) NOT NULL COMMENT ' 权限名称 ',
`enname` varchar(64) NOT NULL COMMENT ' 权限英文名称 ',
`url` varchar(255) NOT NULL COMMENT ' 授权路径 ',
`description` varchar(200) DEFAULT NULL COMMENT ' 备注 ',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8 COMMENT=' 权限表 ';

-- ----------------------------
-- Records of tb_permission
-- ----------------------------
BEGIN;
INSERT INTO `tb_permission` VALUES (37, 0, ' 系统管理 ', 'System', '/', NULL, '2019-04-04 23:22:54', '2019-04-04 23:22:56');
INSERT INTO `tb_permission` VALUES (38, 37, ' 用户管理 ', 'SystemUser', '/users/', NULL, '2019-04-04 23:25:31', '2019-04-04 23:25:33');
INSERT INTO `tb_permission` VALUES (39, 38, ' 查看用户 ', 'SystemUserView', '/users/view/**', NULL, '2019-04-04 15:30:30', '2019-04-04 15:30:43');
INSERT INTO `tb_permission` VALUES (40, 38, ' 新增用户 ', 'SystemUserInsert', '/users/insert/**', NULL, '2019-04-04 15:30:31', '2019-04-04 15:30:44');
INSERT INTO `tb_permission` VALUES (41, 38, ' 编辑用户 ', 'SystemUserUpdate', '/users/update/**', NULL, '2019-04-04 15:30:32', '2019-04-04 15:30:45');
INSERT INTO `tb_permission` VALUES (42, 38, ' 删除用户 ', 'SystemUserDelete', '/users/delete/**', NULL, '2019-04-04 15:30:48', '2019-04-04 15:30:45');
INSERT INTO `tb_permission` VALUES (44, 37, ' 内容管理 ', 'SystemContent', '/contents/', NULL, '2019-04-06 18:23:58', '2019-04-06 18:24:00');
INSERT INTO `tb_permission` VALUES (45, 44, ' 查看内容 ', 'SystemContentView', '/contents/view/**', NULL, '2019-04-06 23:49:39', '2019-04-06 23:49:41');
INSERT INTO `tb_permission` VALUES (46, 44, ' 新增内容 ', 'SystemContentInsert', '/contents/insert/**', NULL, '2019-04-06 23:51:00', '2019-04-06 23:51:02');
INSERT INTO `tb_permission` VALUES (47, 44, ' 编辑内容 ', 'SystemContentUpdate', '/contents/update/**', NULL, '2019-04-06 23:51:04', '2019-04-06 23:51:06');
INSERT INTO `tb_permission` VALUES (48, 44, ' 删除内容 ', 'SystemContentDelete', '/contents/delete/**', NULL, '2019-04-06 23:51:08', '2019-04-06 23:51:10');
COMMIT;

-- ----------------------------
-- Table structure for tb_role
-- ----------------------------
DROP TABLE IF EXISTS `tb_role`;
CREATE TABLE `tb_role` (
`id` bigint NOT NULL AUTO_INCREMENT,
`parent_id` bigint DEFAULT NULL COMMENT ' 父角色 ',
`name` varchar(64) NOT NULL COMMENT ' 角色名称 ',
`enname` varchar(64) NOT NULL COMMENT ' 角色英文名称 ',
`description` varchar(200) DEFAULT NULL COMMENT ' 备注 ',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8 COMMENT=' 角色表 ';

-- ----------------------------
-- Records of tb_role
-- ----------------------------
BEGIN;
INSERT INTO `tb_role` VALUES (37, 0, ' 超级管理员 ', 'admin', ' 管理 Everything', '2019-04-04 23:22:03', '2019-04-04 23:22:05');
INSERT INTO `tb_role` VALUES (38, 0, ' 物料仓库管理员 ', 'warehouse', ' 负责仓库货物管理 ', '2021-05-02 18:25:02', '2021-05-02 18:25:04');
INSERT INTO `tb_role` VALUES (39, 0, ' 人力资源管理员 ', 'hr', ' 负责人力资源管理 ', '2021-05-02 18:26:14', '2021-05-02 18:26:16');
COMMIT;

-- ----------------------------
-- Table structure for tb_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `tb_role_permission`;
CREATE TABLE `tb_role_permission` (
`id` bigint NOT NULL AUTO_INCREMENT,
`role_id` bigint NOT NULL COMMENT ' 角色 ID',
`permission_id` bigint NOT NULL COMMENT ' 权限 ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8 COMMENT=' 角色权限表 ';

-- ----------------------------
-- Records of tb_role_permission
-- ----------------------------
BEGIN;
INSERT INTO `tb_role_permission` VALUES (37, 37, 37);
INSERT INTO `tb_role_permission` VALUES (38, 37, 38);
INSERT INTO `tb_role_permission` VALUES (39, 37, 39);
INSERT INTO `tb_role_permission` VALUES (40, 37, 40);
INSERT INTO `tb_role_permission` VALUES (41, 37, 41);
INSERT INTO `tb_role_permission` VALUES (42, 37, 42);
INSERT INTO `tb_role_permission` VALUES (43, 37, 44);
INSERT INTO `tb_role_permission` VALUES (44, 37, 45);
INSERT INTO `tb_role_permission` VALUES (45, 37, 46);
INSERT INTO `tb_role_permission` VALUES (46, 37, 47);
INSERT INTO `tb_role_permission` VALUES (47, 37, 48);
INSERT INTO `tb_role_permission` VALUES (48, 39, 38);
INSERT INTO `tb_role_permission` VALUES (49, 38, 44);
COMMIT;

-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT ' 用户名 ',
`password` varchar(64) NOT NULL COMMENT ' 密码,加密存储 ',
`phone` varchar(20) DEFAULT NULL COMMENT ' 注册手机号 ',
`email` varchar(50) DEFAULT NULL COMMENT ' 注册邮箱 ',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`) USING BTREE,
UNIQUE KEY `phone` (`phone`) USING BTREE,
UNIQUE KEY `email` (`email`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8 COMMENT=' 用户表 ';

-- ----------------------------
-- Records of tb_user
-- ----------------------------
BEGIN;
INSERT INTO `tb_user` VALUES (37, 'admin', '$2a$10$9ZhDOBp.sRKat4l14ygu/.LscxrMUcDAfeVOEPiYwbcRkoB09gCmi', '15888888888', 'zchanglin@163.com', '2021-05-04 23:21:27', '2021-05-04 23:21:29');
INSERT INTO `tb_user` VALUES (38, 'hello', '$2a$10$9ZhDOBp.sRKat4l14ygu/.LscxrMUcDAfeVOEPiYwbcRkoB09gCmi', '15288888888', '1610984228@qq.com', '2021-05-02 18:21:48', '2021-05-02 18:21:54');
INSERT INTO `tb_user` VALUES (39, 'jone', '$2a$10$9ZhDOBp.sRKat4l14ygu/.LscxrMUcDAfeVOEPiYwbcRkoB09gCmi', '13900009999', 'jone@gmail.com', '2021-05-02 18:22:39', '2021-05-02 18:22:41');
INSERT INTO `tb_user` VALUES (40, 'tim', '$2a$10$9ZhDOBp.sRKat4l14ygu/.LscxrMUcDAfeVOEPiYwbcRkoB09gCmi', '14200007777', 'tim@163.com', '2021-05-02 18:23:09', '2021-05-02 18:23:13');
COMMIT;

-- ----------------------------
-- Table structure for tb_user_role
-- ----------------------------
DROP TABLE IF EXISTS `tb_user_role`;
CREATE TABLE `tb_user_role` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT ' 用户 ID',
`role_id` bigint NOT NULL COMMENT ' 角色 ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8 COMMENT=' 用户角色表 ';

-- ----------------------------
-- Records of tb_user_role
-- ----------------------------
BEGIN;
INSERT INTO `tb_user_role` VALUES (37, 37, 37);
INSERT INTO `tb_user_role` VALUES (38, 40, 39);
INSERT INTO `tb_user_role` VALUES (39, 39, 38);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

代码实现

为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,例如系统管理员、管理员、用户、访客等角色。角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。

认证授权服务器配置

对于 spring-security-oauth2-server(认证授权服务器), 资源服务器需要访问 /oauth/check_token 端点来检查 access_token 的有效性,此时该端点是受保护的资源,当我们访问该端点时会遇到 403 问题,将该端点暴露出来即可,暴露端点的关键代码为,WebSecurityConfiguration.java:

1
2
3
4
@Override
public void configure(WebSecurity web) {
web.ignoring ().antMatchers ("/oauth/check_token");
}

之前介绍过资源的概念,简单点说就是需要被访问的业务数据或是静态资源文件都可以被称作资源。现在单独创建一个名为 spring-security-oauth2-resource 资源服务器的项目,该项目的主要目的就是对数据表的 CRUD 操作,而这些操作就是对资源的操作了,但是为了方便只需要使用两个简单的示例即可,该项目的 pom.xml 如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-security-oauth2</artifactId>
<groupId>cn.tim</groupId>
<version>1.0-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>spring-security-oauth2-resources</artifactId>
<description> 第一个示例资源服务器 < span class="tag"></description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>cn.tim.security.resources.OAuth2ResourceApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>

该项目的唯一一个 Controller 也比较简单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
@RequestMapping ("/users")
public class UserController {

@GetMapping
public String index(){
return "{\n" +
" \"people\":[\n" +
" {\n" +
" \"firstName\":\"Brett\",\n" +
" \"lastName\":\"McLaughlin\"\n" +
" },\n" +
" {\n" +
" \"firstName\":\"Jason\",\n" +
" \"lastName\":\"Hunter\"\n" +
" }\n" +
" ]\n" +
"}";
}
}

资源服务器配置

接下来需要配置资源服务器,创建一个类继承 ResourceServerConfigurerAdapter 并添加相关注解,这个配置类我起名为 ResourceServerConfiguration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.tim.security.resources.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity (prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests ()
.antMatchers ("/users").hasAuthority ("SystemUser")
.antMatchers ("/contents").hasAuthority ("SystemContent")
.antMatchers ("/contents/view/**").hasAuthority ("SystemContentView");
}
}

application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
spring:
application:
name: resource-server-1
server:
port: 8090
servlet:
context-path: /

# 配置的就是认证服务器的认证地址
security:
oauth2:
client:
client-id: client
client-secret: secret
access-token-uri: http://localhost:8080/oauth/token
user-authorization-uri: http://localhost:8080/oauth/authorize
resource:
token-info-uri: http://localhost:8080/oauth/check_token

logging:
level:
root: INFO
org.springframework.web: INFO
org.springframework.security: INFO
org.springframework.security.oauth2: INFO

资源服务器需要访问 /oauth/check_token 端点来检查 access_token 的有效性,此时该端点是受保护的资源,当我们访问该端点时会遇到 403 问题,将该端点暴露出来即可,暴露端点的关键代码是在 spring-security-oauth2-server 的 WebSecurityConfiguration 中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity (prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

@Bean
@Override
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}

@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService (userDetailsService ());
}

@Override
public void configure(WebSecurity web) {
// 将 check_token 暴露出去,否则资源服务器访问时报 403 错误
web.ignoring ().antMatchers ("/oauth/check_token");
}
}

示例项目验证

整个示例项目就完成了,下面还是以前的步骤,先访问获取授权码
打开浏览器,输入地址:

1
GET http://localhost:8080/oauth/authorize?client_id=tencent&response_type=code

登录成功后会询问用户是否授权客户端:

然后拿到授权码 code,根据 code 再请求 access_token:
1
POST http://tencent:secret@localhost:8080/oauth/token


然后再根据 access_token 直接去调用接口就好了:

由于整个工程模块比较多,说起来也比较杂乱,索性直接上传到 Github,https://github.com/zouchanglin/spring-security-oauth2

最后,有没有发现我们已经实现了 SSO 了呢?其实单点登录就这么简单呀!

欢迎关注我的其它发布渠道