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如下:

 1CREATE TABLE `tb_permission`
 2(
 3    `id`          bigint(20)   NOT NULL AUTO_INCREMENT,
 4    `parent_id`   bigint(20)   DEFAULT NULL COMMENT '父权限',
 5    `name`        varchar(64)  NOT NULL COMMENT '权限名称',
 6    `enname`      varchar(64)  NOT NULL COMMENT '权限英文名称',
 7    `url`         varchar(255) NOT NULL COMMENT '授权路径',
 8    `description` varchar(200) DEFAULT NULL COMMENT '备注',
 9    `created`     datetime     NOT NULL,
10    `updated`     datetime     NOT NULL,
11    PRIMARY KEY (`id`)
12) ENGINE = InnoDB
13  AUTO_INCREMENT = 37
14  DEFAULT CHARSET = utf8 COMMENT ='权限表';
15
16CREATE TABLE `tb_role`
17(
18    `id`          bigint(20)  NOT NULL AUTO_INCREMENT,
19    `parent_id`   bigint(20)   DEFAULT NULL COMMENT '父角色',
20    `name`        varchar(64) NOT NULL COMMENT '角色名称',
21    `enname`      varchar(64) NOT NULL COMMENT '角色英文名称',
22    `description` varchar(200) DEFAULT NULL COMMENT '备注',
23    `created`     datetime    NOT NULL,
24    `updated`     datetime    NOT NULL,
25    PRIMARY KEY (`id`)
26) ENGINE = InnoDB
27  AUTO_INCREMENT = 37
28  DEFAULT CHARSET = utf8 COMMENT ='角色表';
29
30CREATE TABLE `tb_role_permission`
31(
32    `id`            bigint(20) NOT NULL AUTO_INCREMENT,
33    `role_id`       bigint(20) NOT NULL COMMENT '角色 ID',
34    `permission_id` bigint(20) NOT NULL COMMENT '权限 ID',
35    PRIMARY KEY (`id`)
36) ENGINE = InnoDB
37  AUTO_INCREMENT = 37
38  DEFAULT CHARSET = utf8 COMMENT ='角色权限表';
39
40CREATE TABLE `tb_user`
41(
42    `id`       bigint(20)  NOT NULL AUTO_INCREMENT,
43    `username` varchar(50) NOT NULL COMMENT '用户名',
44    `password` varchar(64) NOT NULL COMMENT '密码,加密存储',
45    `phone`    varchar(20) DEFAULT NULL COMMENT '注册手机号',
46    `email`    varchar(50) DEFAULT NULL COMMENT '注册邮箱',
47    `created`  datetime    NOT NULL,
48    `updated`  datetime    NOT NULL,
49    PRIMARY KEY (`id`),
50    UNIQUE KEY `username` (`username`) USING BTREE,
51    UNIQUE KEY `phone` (`phone`) USING BTREE,
52    UNIQUE KEY `email` (`email`) USING BTREE
53) ENGINE = InnoDB
54  AUTO_INCREMENT = 37
55  DEFAULT CHARSET = utf8 COMMENT ='用户表';
56
57CREATE TABLE `tb_user_role`
58(
59    `id`      bigint(20) NOT NULL AUTO_INCREMENT,
60    `user_id` bigint(20) NOT NULL COMMENT '用户 ID',
61    `role_id` bigint(20) NOT NULL COMMENT '角色 ID',
62    PRIMARY KEY (`id`)
63) ENGINE = InnoDB
64  AUTO_INCREMENT = 37
65  DEFAULT CHARSET = utf8 COMMENT ='用户角色表';

初始化表数据

  1SET NAMES utf8mb4;
  2SET FOREIGN_KEY_CHECKS = 0;
  3
  4-- ----------------------------
  5-- Table structure for ClientDetails
  6-- ----------------------------
  7DROP TABLE IF EXISTS `ClientDetails`;
  8CREATE TABLE `ClientDetails` (
  9  `appId` varchar(256) NOT NULL,
 10  `resourceIds` varchar(256) DEFAULT NULL,
 11  `appSecret` varchar(256) DEFAULT NULL,
 12  `scope` varchar(256) DEFAULT NULL,
 13  `grantTypes` varchar(256) DEFAULT NULL,
 14  `redirectUrl` varchar(256) DEFAULT NULL,
 15  `authorities` varchar(256) DEFAULT NULL,
 16  `access_token_validity` int DEFAULT NULL,
 17  `refresh_token_validity` int DEFAULT NULL,
 18  `additionalInformation` varchar(4096) DEFAULT NULL,
 19  `autoApproveScopes` varchar(256) DEFAULT NULL,
 20  PRIMARY KEY (`appId`)
 21) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 22
 23-- ----------------------------
 24-- Records of ClientDetails
 25-- ----------------------------
 26BEGIN;
 27COMMIT;
 28
 29-- ----------------------------
 30-- Table structure for oauth_access_token
 31-- ----------------------------
 32DROP TABLE IF EXISTS `oauth_access_token`;
 33CREATE TABLE `oauth_access_token` (
 34  `token_id` varchar(256) DEFAULT NULL,
 35  `token` blob,
 36  `authentication_id` varchar(256) NOT NULL,
 37  `user_name` varchar(256) DEFAULT NULL,
 38  `client_id` varchar(256) DEFAULT NULL,
 39  `authentication` blob,
 40  `refresh_token` varchar(256) DEFAULT NULL,
 41  PRIMARY KEY (`authentication_id`)
 42) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 43
 44-- ----------------------------
 45-- Table structure for oauth_approvals
 46-- ----------------------------
 47DROP TABLE IF EXISTS `oauth_approvals`;
 48CREATE TABLE `oauth_approvals` (
 49  `userId` varchar(256) DEFAULT NULL,
 50  `clientId` varchar(256) DEFAULT NULL,
 51  `scope` varchar(256) DEFAULT NULL,
 52  `status` varchar(10) DEFAULT NULL,
 53  `expiresAt` timestamp NULL DEFAULT NULL,
 54  `lastModifiedAt` timestamp NULL DEFAULT NULL
 55) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 56
 57-- ----------------------------
 58-- Records of oauth_approvals
 59-- ----------------------------
 60BEGIN;
 61COMMIT;
 62
 63-- ----------------------------
 64-- Table structure for oauth_client_details
 65-- ----------------------------
 66DROP TABLE IF EXISTS `oauth_client_details`;
 67CREATE TABLE `oauth_client_details` (
 68  `client_id` varchar(256) NOT NULL,
 69  `resource_ids` varchar(256) DEFAULT NULL,
 70  `client_secret` varchar(256) DEFAULT NULL,
 71  `scope` varchar(256) DEFAULT NULL,
 72  `authorized_grant_types` varchar(256) DEFAULT NULL,
 73  `web_server_redirect_uri` varchar(256) DEFAULT NULL,
 74  `authorities` varchar(256) DEFAULT NULL,
 75  `access_token_validity` int DEFAULT NULL,
 76  `refresh_token_validity` int DEFAULT NULL,
 77  `additional_information` varchar(4096) DEFAULT NULL,
 78  `autoapprove` varchar(256) DEFAULT NULL,
 79  PRIMARY KEY (`client_id`)
 80) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 81
 82-- ----------------------------
 83-- Records of oauth_client_details
 84-- ----------------------------
 85BEGIN;
 86INSERT INTO `oauth_client_details` VALUES ('client', NULL, '$2a$10$O0BSekA2psUwp5.KyrmPDOBrP60iiFtsl2vOrXyEpW3uPcW8Jd0xi', 'app', 'authorization_code', 'https://example.com', NULL, NULL, NULL, NULL, NULL);
 87INSERT INTO `oauth_client_details` VALUES ('tencent', NULL, '$2a$10$O0BSekA2psUwp5.KyrmPDOBrP60iiFtsl2vOrXyEpW3uPcW8Jd0xi', 'all_info', 'authorization_code', 'https://qq.com', NULL, NULL, NULL, NULL, NULL);
 88COMMIT;
 89
 90-- ----------------------------
 91-- Table structure for oauth_client_token
 92-- ----------------------------
 93DROP TABLE IF EXISTS `oauth_client_token`;
 94CREATE TABLE `oauth_client_token` (
 95  `token_id` varchar(256) DEFAULT NULL,
 96  `token` blob,
 97  `authentication_id` varchar(256) NOT NULL,
 98  `user_name` varchar(256) DEFAULT NULL,
 99  `client_id` varchar(256) DEFAULT NULL,
100  PRIMARY KEY (`authentication_id`)
101) ENGINE=InnoDB DEFAULT CHARSET=utf8;
102
103-- ----------------------------
104-- Records of oauth_client_token
105-- ----------------------------
106BEGIN;
107COMMIT;
108
109-- ----------------------------
110-- Table structure for oauth_code
111-- ----------------------------
112DROP TABLE IF EXISTS `oauth_code`;
113CREATE TABLE `oauth_code` (
114  `code` varchar(256) DEFAULT NULL,
115  `authentication` blob
116) ENGINE=InnoDB DEFAULT CHARSET=utf8;
117
118-- ----------------------------
119-- Records of oauth_code
120-- ----------------------------
121BEGIN;
122COMMIT;
123
124-- ----------------------------
125-- Table structure for oauth_refresh_token
126-- ----------------------------
127DROP TABLE IF EXISTS `oauth_refresh_token`;
128CREATE TABLE `oauth_refresh_token` (
129  `token_id` varchar(256) DEFAULT NULL,
130  `token` blob,
131  `authentication` blob
132) ENGINE=InnoDB DEFAULT CHARSET=utf8;
133
134-- ----------------------------
135-- Records of oauth_refresh_token
136-- ----------------------------
137BEGIN;
138COMMIT;
139
140-- ----------------------------
141-- Table structure for tb_permission
142-- ----------------------------
143DROP TABLE IF EXISTS `tb_permission`;
144CREATE TABLE `tb_permission` (
145  `id` bigint NOT NULL AUTO_INCREMENT,
146  `parent_id` bigint DEFAULT NULL COMMENT '父权限',
147  `name` varchar(64) NOT NULL COMMENT '权限名称',
148  `enname` varchar(64) NOT NULL COMMENT '权限英文名称',
149  `url` varchar(255) NOT NULL COMMENT '授权路径',
150  `description` varchar(200) DEFAULT NULL COMMENT '备注',
151  `created` datetime NOT NULL,
152  `updated` datetime NOT NULL,
153  PRIMARY KEY (`id`)
154) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8 COMMENT='权限表';
155
156-- ----------------------------
157-- Records of tb_permission
158-- ----------------------------
159BEGIN;
160INSERT INTO `tb_permission` VALUES (37, 0, '系统管理', 'System', '/', NULL, '2019-04-04 23:22:54', '2019-04-04 23:22:56');
161INSERT INTO `tb_permission` VALUES (38, 37, '用户管理', 'SystemUser', '/users/', NULL, '2019-04-04 23:25:31', '2019-04-04 23:25:33');
162INSERT INTO `tb_permission` VALUES (39, 38, '查看用户', 'SystemUserView', '/users/view/**', NULL, '2019-04-04 15:30:30', '2019-04-04 15:30:43');
163INSERT INTO `tb_permission` VALUES (40, 38, '新增用户', 'SystemUserInsert', '/users/insert/**', NULL, '2019-04-04 15:30:31', '2019-04-04 15:30:44');
164INSERT INTO `tb_permission` VALUES (41, 38, '编辑用户', 'SystemUserUpdate', '/users/update/**', NULL, '2019-04-04 15:30:32', '2019-04-04 15:30:45');
165INSERT INTO `tb_permission` VALUES (42, 38, '删除用户', 'SystemUserDelete', '/users/delete/**', NULL, '2019-04-04 15:30:48', '2019-04-04 15:30:45');
166INSERT INTO `tb_permission` VALUES (44, 37, '内容管理', 'SystemContent', '/contents/', NULL, '2019-04-06 18:23:58', '2019-04-06 18:24:00');
167INSERT INTO `tb_permission` VALUES (45, 44, '查看内容', 'SystemContentView', '/contents/view/**', NULL, '2019-04-06 23:49:39', '2019-04-06 23:49:41');
168INSERT INTO `tb_permission` VALUES (46, 44, '新增内容', 'SystemContentInsert', '/contents/insert/**', NULL, '2019-04-06 23:51:00', '2019-04-06 23:51:02');
169INSERT INTO `tb_permission` VALUES (47, 44, '编辑内容', 'SystemContentUpdate', '/contents/update/**', NULL, '2019-04-06 23:51:04', '2019-04-06 23:51:06');
170INSERT INTO `tb_permission` VALUES (48, 44, '删除内容', 'SystemContentDelete', '/contents/delete/**', NULL, '2019-04-06 23:51:08', '2019-04-06 23:51:10');
171COMMIT;
172
173-- ----------------------------
174-- Table structure for tb_role
175-- ----------------------------
176DROP TABLE IF EXISTS `tb_role`;
177CREATE TABLE `tb_role` (
178  `id` bigint NOT NULL AUTO_INCREMENT,
179  `parent_id` bigint DEFAULT NULL COMMENT '父角色',
180  `name` varchar(64) NOT NULL COMMENT '角色名称',
181  `enname` varchar(64) NOT NULL COMMENT '角色英文名称',
182  `description` varchar(200) DEFAULT NULL COMMENT '备注',
183  `created` datetime NOT NULL,
184  `updated` datetime NOT NULL,
185  PRIMARY KEY (`id`)
186) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8 COMMENT='角色表';
187
188-- ----------------------------
189-- Records of tb_role
190-- ----------------------------
191BEGIN;
192INSERT INTO `tb_role` VALUES (37, 0, '超级管理员', 'admin', '管理Everything', '2019-04-04 23:22:03', '2019-04-04 23:22:05');
193INSERT INTO `tb_role` VALUES (38, 0, '物料仓库管理员', 'warehouse', '负责仓库货物管理', '2021-05-02 18:25:02', '2021-05-02 18:25:04');
194INSERT INTO `tb_role` VALUES (39, 0, '人力资源管理员', 'hr', '负责人力资源管理', '2021-05-02 18:26:14', '2021-05-02 18:26:16');
195COMMIT;
196
197-- ----------------------------
198-- Table structure for tb_role_permission
199-- ----------------------------
200DROP TABLE IF EXISTS `tb_role_permission`;
201CREATE TABLE `tb_role_permission` (
202  `id` bigint NOT NULL AUTO_INCREMENT,
203  `role_id` bigint NOT NULL COMMENT '角色 ID',
204  `permission_id` bigint NOT NULL COMMENT '权限 ID',
205  PRIMARY KEY (`id`)
206) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8 COMMENT='角色权限表';
207
208-- ----------------------------
209-- Records of tb_role_permission
210-- ----------------------------
211BEGIN;
212INSERT INTO `tb_role_permission` VALUES (37, 37, 37);
213INSERT INTO `tb_role_permission` VALUES (38, 37, 38);
214INSERT INTO `tb_role_permission` VALUES (39, 37, 39);
215INSERT INTO `tb_role_permission` VALUES (40, 37, 40);
216INSERT INTO `tb_role_permission` VALUES (41, 37, 41);
217INSERT INTO `tb_role_permission` VALUES (42, 37, 42);
218INSERT INTO `tb_role_permission` VALUES (43, 37, 44);
219INSERT INTO `tb_role_permission` VALUES (44, 37, 45);
220INSERT INTO `tb_role_permission` VALUES (45, 37, 46);
221INSERT INTO `tb_role_permission` VALUES (46, 37, 47);
222INSERT INTO `tb_role_permission` VALUES (47, 37, 48);
223INSERT INTO `tb_role_permission` VALUES (48, 39, 38);
224INSERT INTO `tb_role_permission` VALUES (49, 38, 44);
225COMMIT;
226
227-- ----------------------------
228-- Table structure for tb_user
229-- ----------------------------
230DROP TABLE IF EXISTS `tb_user`;
231CREATE TABLE `tb_user` (
232  `id` bigint NOT NULL AUTO_INCREMENT,
233  `username` varchar(50) NOT NULL COMMENT '用户名',
234  `password` varchar(64) NOT NULL COMMENT '密码,加密存储',
235  `phone` varchar(20) DEFAULT NULL COMMENT '注册手机号',
236  `email` varchar(50) DEFAULT NULL COMMENT '注册邮箱',
237  `created` datetime NOT NULL,
238  `updated` datetime NOT NULL,
239  PRIMARY KEY (`id`),
240  UNIQUE KEY `username` (`username`) USING BTREE,
241  UNIQUE KEY `phone` (`phone`) USING BTREE,
242  UNIQUE KEY `email` (`email`) USING BTREE
243) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8 COMMENT='用户表';
244
245-- ----------------------------
246-- Records of tb_user
247-- ----------------------------
248BEGIN;
249INSERT 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');
250INSERT 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');
251INSERT 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');
252INSERT 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');
253COMMIT;
254
255-- ----------------------------
256-- Table structure for tb_user_role
257-- ----------------------------
258DROP TABLE IF EXISTS `tb_user_role`;
259CREATE TABLE `tb_user_role` (
260  `id` bigint NOT NULL AUTO_INCREMENT,
261  `user_id` bigint NOT NULL COMMENT '用户 ID',
262  `role_id` bigint NOT NULL COMMENT '角色 ID',
263  PRIMARY KEY (`id`)
264) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8 COMMENT='用户角色表';
265
266-- ----------------------------
267-- Records of tb_user_role
268-- ----------------------------
269BEGIN;
270INSERT INTO `tb_user_role` VALUES (37, 37, 37);
271INSERT INTO `tb_user_role` VALUES (38, 40, 39);
272INSERT INTO `tb_user_role` VALUES (39, 39, 38);
273COMMIT;
274
275SET FOREIGN_KEY_CHECKS = 1;

代码实现

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

认证授权服务器配置

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

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

之前介绍过资源的概念,简单点说就是需要被访问的业务数据或是静态资源文件都可以被称作资源。现在单独创建一个名为 spring-security-oauth2-resource 资源服务器的项目,该项目的主要目的就是对数据表的 CRUD 操作,而这些操作就是对资源的操作了,但是为了方便只需要使用两个简单的示例即可,该项目的 pom.xml 如下:

 1<?xml version="1.0" encoding="UTF-8"?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0"
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5    <parent>
 6        <artifactId>spring-security-oauth2</artifactId>
 7        <groupId>cn.tim</groupId>
 8        <version>1.0-SNAPSHOT</version>
 9    </parent>
10
11    <modelVersion>4.0.0</modelVersion>
12
13    <artifactId>spring-security-oauth2-resources</artifactId>
14    <description>第一个示例资源服务器</description>
15    <properties>
16        <maven.compiler.source>8</maven.compiler.source>
17        <maven.compiler.target>8</maven.compiler.target>
18    </properties>
19
20    <dependencies>
21        <dependency>
22            <groupId>org.springframework.boot</groupId>
23            <artifactId>spring-boot-starter-web</artifactId>
24        </dependency>
25
26        <dependency>
27            <groupId>org.springframework.boot</groupId>
28            <artifactId>spring-boot-starter-test</artifactId>
29        </dependency>
30
31        <dependency>
32            <groupId>org.springframework.cloud</groupId>
33            <artifactId>spring-cloud-starter-oauth2</artifactId>
34        </dependency>
35    </dependencies>
36
37    <build>
38        <plugins>
39            <plugin>
40                <groupId>org.springframework.boot</groupId>
41                <artifactId>spring-boot-maven-plugin</artifactId>
42                <configuration>
43                    <mainClass>cn.tim.security.resources.OAuth2ResourceApplication</mainClass>
44                </configuration>
45            </plugin>
46        </plugins>
47    </build>
48</project>

该项目的唯一一个 Controller 也比较简单

 1@RestController
 2@RequestMapping("/users")
 3public class UserController {
 4
 5    @GetMapping
 6    public String index(){
 7        return "{\n" +
 8                "    \"people\":[\n" +
 9                "        {\n" +
10                "            \"firstName\":\"Brett\",\n" +
11                "            \"lastName\":\"McLaughlin\"\n" +
12                "        },\n" +
13                "        {\n" +
14                "            \"firstName\":\"Jason\",\n" +
15                "            \"lastName\":\"Hunter\"\n" +
16                "        }\n" +
17                "    ]\n" +
18                "}";
19    }
20}

资源服务器配置

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

 1package cn.tim.security.resources.config;
 2
 3import org.springframework.context.annotation.Configuration;
 4import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 5import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 6import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
 7import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
 8
 9@Configuration
10@EnableResourceServer
11@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
12public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
13
14    @Override
15    public void configure(HttpSecurity http) throws Exception {
16        http.authorizeRequests()
17                .antMatchers("/users").hasAuthority("SystemUser")
18                .antMatchers("/contents").hasAuthority("SystemContent")
19                .antMatchers("/contents/view/**").hasAuthority("SystemContentView");
20    }
21}

application.yml

 1spring:
 2  application:
 3    name: resource-server-1
 4server:
 5  port: 8090
 6  servlet:
 7    context-path: /
 8
 9# 配置的就是认证服务器的认证地址
10security:
11  oauth2:
12    client:
13      client-id: client
14      client-secret: secret
15      access-token-uri: http://localhost:8080/oauth/token
16      user-authorization-uri: http://localhost:8080/oauth/authorize
17    resource:
18      token-info-uri: http://localhost:8080/oauth/check_token
19
20logging:
21  level:
22    root: INFO
23    org.springframework.web: INFO
24    org.springframework.security: INFO
25    org.springframework.security.oauth2: INFO

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

 1@Configuration
 2@EnableWebSecurity
 3@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
 4public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
 5
 6    @Bean
 7    @Override
 8    public UserDetailsService userDetailsService() {
 9        return new UserDetailsServiceImpl();
10    }
11
12    @Bean
13    public BCryptPasswordEncoder passwordEncoder(){
14        return new BCryptPasswordEncoder();
15    }
16
17    @Override
18    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
19        auth.userDetailsService(userDetailsService());
20    }
21
22    @Override
23    public void configure(WebSecurity web) {
24      // 将 check_token 暴露出去,否则资源服务器访问时报 403 错误
25        web.ignoring().antMatchers("/oauth/check_token");
26    }
27}

示例项目验证

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

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

登录成功后会询问用户是否授权客户端: 然后拿到授权码code,根据code再请求 access_token:

1POST http://tencent:secret@localhost:8080/oauth/token

然后再根据 access_token 直接去调用接口就好了: 由于整个工程模块比较多,说起来也比较杂乱,索性直接上传到Github, https://github.com/zouchanglin/spring-security-oauth2

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