This article looks at role-based Access Control (RBAC), where users associate permissions with roles. Simply put, a user has several roles, and each role has several permissions. This constructs a user-role-permission authorization model. In this model, the relationship between users and roles and between roles and permissions is generally many-to-many. After understanding RBAC, Spring Security oAuth2 is used to implement custom authentication based on RBAC.
Summary of RBAC
RBAC believes that authorization is actually the relationship between Who, What and How triples, that is, Who performs How operation on What, that is, “subject” performs operation on “object”. Who: indicates the owner or principal (for example, User or Role) of the permission. What: Indicates operation or object. How: Specific permissions (Privilege, positive and negative authorization)
In our oAuth2 system, we need to control the permissions of all resources in the system. Resources in the system include: 1, static resources (object resources) : functional operations, data columns 2, dynamic resources (data resources) : articles, albums, notes
The purpose of the system is to control the permissions of all object resources and data resources of the application system, such as function menus, interface buttons, data display columns, and various row-level data.
Create table
Therefore, according to the above definition, at least five tables are required: ① User user table, ② role role table, ③ Permission permission table, ④ user_Role user-role table, and ⑤ ROLE_PERMISSION role-permission table
The SQL is as follows:
CREATE TABLE `tb_permission` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, 'parent_id' bigint(20) DEFAULT NULL COMMENT 'parent ',' name 'varchar(64) NOT NULL COMMENT' parent ', 'enname' varchar(64) NOT NULL COMMENT 'iD ',' url 'varchar(255) NOT NULL COMMENT' id ', 'description' vARCHar (200) DEFAULT NULL COMMENT 'created' datetime NOT NULL, 'added' datetime NOT NULL, PRIMARY KEY (' id ') ENGINE = InnoDB AUTO_INCREMENT = 37 DEFAULT CHARSET = utf8 COMMENT =' privileges '; CREATE TABLE 'tb_role' (' id 'bigint(20) NOT NULL AUTO_INCREMENT,' parent_id 'bigint(20) DEFAULT NULL COMMENT' parent ', 'name' varchar(64) NOT NULL COMMENT 'varchar ',' enname 'varchar(64) NOT NULL COMMENT' varchar ', 'description' vARCHar (200) DEFAULT NULL COMMENT 'created' datetime NOT NULL, 'added' datetime NOT NULL, PRIMARY KEY (' id ') ENGINE = InnoDB AUTO_INCREMENT = 37 DEFAULT CHARSET = utf8 COMMENT =' table '; 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 =' mysql '; CREATE TABLE 'tb_user' (' id 'bigint(20) NOT NULL AUTO_INCREMENT,' username 'varchar(50) NOT NULL COMMENT' username ', 'password' varchar(64) NOT NULL COMMENT ' ', 'phone' varchar(20) DEFAULT NULL COMMENT 'register phone ',' email 'varchar(50) DEFAULT NULL COMMENT' register email ', `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) USING BTREE, UNIQUE KEY `phone` (`phone`) USING BTREE, InnoDB AUTO_INCREMENT = 37 DEFAULT CHARSET = utf8 COMMENT =' 10 '; CREATE TABLE 'tb_user_role' (' id 'bigint(20) NOT NULL AUTO_INCREMENT,' user_id 'bigint(20) NOT NULL COMMENT' user ID', 'role_id' bigint(20) NOT NULL COMMENT 'ID', PRIMARY KEY (' id ') ENGINE = InnoDB AUTO_INCREMENT = 37 DEFAULT CHARSET = utf8 COMMENT =' mysql ';Copy the code
Initialize table data
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', '[email protected]', '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', '[email protected]', '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', '[email protected]', '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', '[email protected]', '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;
Copy the code
Code implementation
To manage users with similar rights, roles are defined, such as system administrator, administrator, user, and visitor. A role has a hierarchical relationship and can form a tree view. The permissions of the parent role are the sum of the permissions of the parent role and all its children. Users of the parent role and groups of the parent role can be promoted in the same way.
Authentication and authorization server configuration
For spring-Security-OAuth2-Server, the resource server needs to access the/oAuth /check_token endpoint to check the validity of the Access_token. This endpoint is a protected resource. 403 problems when we visit the endpoint, the endpoint can be exposed, exposed the endpoint for the key code, WebSecurityConfiguration. Java:
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/oauth/check_token");
}
Copy the code
The concept of a resource was introduced earlier, which simply means that business data or static resource files that need to be accessed can be referred to as resources. Now create a separate project called Spring-security-OAuth2-Resource resource server. The main purpose of this project is to perform CRUD operations on tables, and these operations are on resources, but just two simple examples are needed for convenience. The pom.xml for the project is as follows:
<? The 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" > < the parent > < artifactId > spring ws-security - oauth2 < / artifactId > < groupId > cn. Tim < / groupId > < version > 1.0 - the SNAPSHOT < / version > < / parent > The < modelVersion > 4.0.0 < / modelVersion > < artifactId > spring ws-security - oauth2 - resources < / artifactId > <description> First example resource server </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>Copy the code
The project’s single Controller is also relatively simple
@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" + "}"; }}Copy the code
Resource Server Configuration
Then need to configure the server resources, create a class inherits ResourceServerConfigurerAdapter and add related note, the configuration class I called ResourceServerConfiguration:
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"); }}Copy the code
application.yml
Spring: application: name: resource-server-1 server: port: 8090 servlet: context-path: / # 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: INFOCopy the code
The resource server needs to access the /oauth/check_token endpoint to check the validity of the access_token. At this point, the endpoint is a protected resource. When we access this endpoint, we will encounter 403 problems. The key code to expose the endpoint is in the WebSecurityConfiguration of spring-Security-OAuth2-Server:
@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) { Ignoring ().antmatchers ("/oauth/check_token"); }}Copy the code
Sample project validation
Now that the sample project is complete, the previous steps are as follows: Access to obtain the authorization code open a browser and type the address:
GET http://localhost:8080/oauth/authorize? client_id=tencent&response_type=codeCopy the code
After successful login, the user is asked whether to authorize the client:Access_token = access_token = access_token
POST http://tencent:secret@localhost:8080/oauth/token
Copy the code
Access_token = access_token = access_token;Since the whole project module is quite many, it is also quite messy to say, so I just upload it to Github directly.Github.com/zouchanglin…