🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 基础Web工程的建设 ### 创建父项目 > 命名:cloudframe ```xml <?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"> <modelVersion>4.0.0</modelVersion> <groupId>com.gosuncn</groupId> <artifactId>cloudframe</artifactId> <version>1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR1</spring-cloud.version> <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring.cloud.alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project> ``` ### 创建公共子模块 > 命名:common ```xml <?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>cloudframe</artifactId> <groupId>com.gosuncn</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>common</artifactId> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> </project> ``` 此时父项目自动更改 ```xml ... <groupId>com.gosuncn</groupId> <artifactId>cloudframe</artifactId> <packaging>pom</packaging> <version>1.0</version> <modules> <module>common</module> </modules> ... ``` ##### 创建Main方法函数 ```java package com.gosuncn; public class MainStarter { public static void main(String[] args) { } } ``` ##### 创建封装响应类 ```java package com.gosuncn.bean; @Data @NoArgsConstructor @AllArgsConstructor public class CommonResult<T> implements Serializable { /** * 状态码 */ private Integer code; /** * 状态说明 */ private String msg; /** * 数据内容 */ private T data; } ``` 其他模块引入公用模块可以共用此类来封装响应。 ### 创建认证授权模块 > 命名:authorize ```xml <?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>cloudframe</artifactId> <groupId>com.gosuncn</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>authorize</artifactId> <dependencies> <dependency> <groupId>com.gosuncn</groupId> <artifactId>common</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project> ``` ##### 创建测试控制器 ```java package com.gosuncn.controller; @RestController public class UserController { @GetMapping("/users") public CommonResult getUsers() { return new CommonResult(HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase(), "Hello, I'm Apollo."); } } ``` ##### 创建配置文件 > application.yml ```yaml server: port: 80 ``` ##### 测试 ```http http://127.0.0.1/users ``` ```json {"code":200,"msg":"OK","data":"Hello, I'm Apollo."} ``` # 创建数据库表结构 ### 创建表结构 创建`sys_rbac`数据库。在库中,创建RBAC经典表结构:用户表、角色表、权限表以及两张中间表,最后创建一张表`oauth_client_details`,它用来存储第三方客户端信息,主要用于OAuth2协议。 ```mysql CREATE DATABASE `sys_rbac` CHARACTER SET 'utf8' COLLATE 'utf8_bin'; USE `sys_rbac`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户名', `password` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '密码', `enabled` tinyint(1) NULL DEFAULT 1 COMMENT '启用状态[1:启用][0:未启用]', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `index_user_username`(`username`) USING BTREE COMMENT '用户名唯一索引' ); CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色ID', `name` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '角色名称', `desc` char(64) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '角色描述', PRIMARY KEY (`id`) USING BTREE ); CREATE TABLE `access` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '权限ID', `name` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '权限名称', `desc` char(64) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '权限描述', PRIMARY KEY (`id`) USING BTREE ); CREATE TABLE `user_role` ( `user_id` int(11) NOT NULL COMMENT '用户ID', `role_id` int(11) NOT NULL COMMENT '角色ID', INDEX `fk_user_role_role_id`(`role_id`) USING BTREE, INDEX `fk_user_role_user_id`(`user_id`) USING BTREE, CONSTRAINT `fk_user_role_role_id` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_user_role_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ); CREATE TABLE `role_access` ( `role_id` int(11) NOT NULL COMMENT '角色ID', `access_id` int(11) NOT NULL COMMENT '权限ID', INDEX `fk_role_access_role_id`(`role_id`) USING BTREE, INDEX `fk_role_access_access_id`(`access_id`) USING BTREE, CONSTRAINT `fk_role_access_access_id` FOREIGN KEY (`access_id`) REFERENCES `access` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_role_access_role_id` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ); CREATE TABLE `oauth_client_details` ( `client_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, `client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, `scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, `authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, `web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, `authorities` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, `access_token_validity` int(11) NULL DEFAULT NULL, `refresh_token_validity` int(11) NULL DEFAULT NULL, `additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, `autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, PRIMARY KEY (`client_id`) USING BTREE ); ``` `authorized_grant_types`授权类型可选值有:`authorization_code`,`implicit`,`password`,`client_credentials`,`refresh_token` 多个值用`,`分割。 ### 插入模拟数据 ```mysql INSERT INTO `sys_rbac`.`user` ( `id`, `username`, `password`, `enabled` ) VALUES ( 1, 'user', '$2a$10$1hZj6YmqJ81Dkpuitsl3S.CHVzJXJN2b5srKMgGexLzFQU5jlZbgu', 1 ); INSERT INTO `sys_rbac`.`role`(`id`, `name`, `desc`) VALUES (1, 'ADMIN', '管理员'); INSERT INTO `sys_rbac`.`access`(`id`, `name`, `desc`) VALUES (1, 'all', '测试数据'); INSERT INTO `sys_rbac`.`user_role`(`user_id`, `role_id`) VALUES (1, 1); INSERT INTO `sys_rbac`.`role_access`(`role_id`, `access_id`) VALUES (1, 1); INSERT INTO `sys_rbac`.`oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types` , `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information` , `autoapprove`) VALUES ('web', NULL, '$2a$10$fvyMIE99J59EBj1K/huJ0ethuO7LnAfKFwjuUl5Pz36zLUw61UWie', 'all', 'authorization_code,implicit,password,client_credentials,refresh_token' , 'http://www.baidu.com', NULL, NULL, NULL, NULL , 'true'); ``` # 引入MyBatis框架 ### 添加MyBatis相关依赖 ```xml <?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>cloudframe</artifactId> <groupId>com.gosuncn</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>authorize</artifactId> <dependencies> <dependency> <groupId>com.gosuncn</groupId> <artifactId>common</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>com/gosuncn/dao/*.xml</include> </includes> </resource> </resources> </build> </project> ``` 其中,`<build>`···`</build>`这段是为了maven可将mapper的xml文件可与dao接口写在一起。 ### 添加数据源和mybatis配置信息 ```yaml server: port: 80 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///sys_rbac?serverTimezone=UTC username: root password: root mybatis: mapper-locations: classpath*:/com/gosuncn/dao/*.xml ``` ### 创建实体类 ##### 创建user表对应实体类User类 ```java package com.gosuncn.entity; @Data public class User { private Integer id; private String username; private String password; private Boolean enabled; } ``` ##### 创建role表对应实体类Role类 ```java package com.gosuncn.entity; @Data public class Role { private Integer id; private String name; private String desc; } ``` ##### 创建access表对应实体类Access类 ```java package com.gosuncn.entity; @Data public class Access { private Integer id; private String name; private String desc; } ``` ### 创建UserMapper接口 ```java package com.gosuncn.dao; @Mapper public interface UserMapper { User getUserByUsername(@Param("username") String username); } ``` ### 创建UserMapper.xml文件 ```xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.gosuncn.dao.UserMapper"> <select id="getUserByUsername" parameterType="java.lang.String" resultType="com.gosuncn.entity.User"> SELECT `id`, `username`, `password`, `enabled` FROM `user` WHERE `username` = #{username} </select> </mapper> ``` ### 创建UserService类 ```java package com.gosuncn.service; @Service public class UserService { @Autowired private UserMapper userMapper; Optional<User> getUserByUsername(String username) { return Optional.ofNullable(userMapper.getUserByUsername(username)); } } ``` ### 修改UserController类 ```java package com.gosuncn.controller; @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/user") public CommonResult getUserByUsername(@RequestParam String username) { return new CommonResult(HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase(), userService.getUserByUsername(username).orElse(null)); } } ``` ### 启动项目测试 ##### 访问存在的用户 ```http http://127.0.0.1/user?username=user ``` ```json { "code": 200, "msg": "OK", "data": { "id": 1, "username": "user", "password": "$2a$10$1hZj6YmqJ81Dkpuitsl3S.CHVzJXJN2b5srKMgGexLzFQU5jlZbgu", "enabled": true } } ``` ##### 访问不存在的用户 ```http http://127.0.0.1/user?username=Apollo ``` ```json {"code":200,"msg":"OK","data":null} ``` # 生成PrivateKey ### 生成私钥命令 因为在oauth2协议中,我们用到了非对称加密算法,因此,我们先生成放在认证服务器端的私钥: ```shell keytool -genkeypair -alias cigcjks -keyalg RSA -keypass cigcjks -keystore cigc.jks -storepass cigcjks ``` 通过上面命令,我们得到了一个`cigc.jks`文件。 ### 具体说明 ```shell keytool -genkeypair -alias keyname -keyalg RSA -keypass keyword -keystore file.jks -storepass fileword ``` - keyname 生成的密钥对的名称 - keyword 生成的密钥对的密码 - file.jks 生成的密钥对的文件名 - fileword 生成的密钥对的文件名的密码 > 其中,`keyname、keyword、file.jks、fileword`均是可以自己定义修改的,建议遵守常规命名规范。示例: > > keytool -genkeypair -alias key0001 -keyalg RSA -keypass wVsE6k -keystore cigc.jks -storepass TX0zAg --- ### 安置私钥 在项目的`resources`目录下,新建一个名为`PrivateKey`的目录。将`cigc.jks`文件放入`PrivateKey`目录中,以备后用。 # 生成PublicKey ### 生成公钥命令 公钥是放在资源服务器上的文件,目前先做出来准备后用。 ```shell keytool -list -rfc --keystore file.jks | openssl x509 -inform pem -pubkey ``` - file.jks 生成的密钥对的文件名 > 示例: > > keytool -list -rfc --keystore cigc.jks | openssl x509 -inform pem -pubkey --- ### 演示例子 接下来,我们就执行命令,记住了,一定在与`jks`文件同级目录下,否则找不到`jks`文件。 ```shell keytool -list -rfc --keystore cigc.jks | openssl x509 -inform pem -pubkey ``` 输入密码:cigcjks(生成PrivateKey这一步时,设置的密码。)得到: ``` -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMQg2pRayMgZD3E8z4iD ev7FpOk5ljyiF3jUNPLcPtX2fnEYQsQTc8af85Mk+Q8jH5icqlkJOs+0KsBH899m AjWNU6yppySq5jklp5vg3TMK+ShO4VRC+/b2aJwqbGYoPHDrgMu7xpl3eaFJiwcj oeNIsPQYTL5UlzROGFjO0CkXaRRq/sRHhJbRL2kBey8i/+gB8GACpgq/GeRREJu0 XzLO5sgSeRG5OyzNeURvCS2D9mtx4o86WDicIjev3hVjubKyhNumILk/ZV4zPQy/ k2T+DnyoapAFg/6xK2NxQdkFEu7/kma9tde6Uj1QDVvzXf89ZWnPSPEzaHVACzZk pwIDAQAB -----END PUBLIC KEY----- -----BEGIN CERTIFICATE----- MIIDUzCCAjugAwIBAgIEEUTVCjANBgkqhkiG9w0BAQsFADBaMQ0wCwYDVQQGEwRj aWdjMQ0wCwYDVQQIEwRjaWdjMQ0wCwYDVQQHEwRjaWdjMQ0wCwYDVQQKEwRjaWdj MQ0wCwYDVQQLEwRjaWdjMQ0wCwYDVQQDEwRjaWdjMB4XDTIwMDQyODA2NTM1MFoX DTIwMDcyNzA2NTM1MFowWjENMAsGA1UEBhMEY2lnYzENMAsGA1UECBMEY2lnYzEN MAsGA1UEBxMEY2lnYzENMAsGA1UEChMEY2lnYzENMAsGA1UECxMEY2lnYzENMAsG A1UEAxMEY2lnYzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALjEINqU WsjIGQ9xPM+Ig3r+xaTpOZY8ohd41DTy3D7V9n5xGELEE3PGn/OTJPkPIx+YnKpZ CTrPtCrAR/PfZgI1jVOsqackquY5Jaeb4N0zCvkoTuFUQvv29micKmxmKDxw64DL u8aZd3mhSYsHI6HjSLD0GEy+VJc0ThhYztApF2kUav7ER4SW0S9pAXsvIv/oAfBg AqYKvxnkURCbtF8yzubIEnkRuTsszXlEbwktg/ZrceKPOlg4nCI3r94VY7mysoTb piC5P2VeMz0Mv5Nk/g58qGqQBYP+sStjcUHZBRLu/5JmvbXXulI9UA1b813/PWVp z0jxM2h1QAs2ZKcCAwEAAaMhMB8wHQYDVR0OBBYEFDJG8MKxT5rgKgtfRIK9iPSx ZejzMA0GCSqGSIb3DQEBCwUAA4IBAQAE/5ouShSwaOIrq13FZt9VDzAZdyXjZ5Ly wImfH+bsdtIRB3wbRml3fNClw9gfivwbAuvVZb11nJM3bdrzUcKVnzd7dvXRj9w4 /aaXcyVjBwE6uBCyMn2k8enOdf1BNkAurS/yhmPo4lCrJS70LNAw+y7dw+dQVuKa wUiJ8330cIRKMhll/kFIVE6np55tPG2jAEY63WnN/bDDHRZIUQplqfe6YGOmG5wu 5ViZdAmRRtaC/g51SzrIvmiJV5CdI5s4FOt8r2QCXKLBbLtYlhSZONc471HWpRz5 P2vb4cg8UjlaUEH0j4u3biCaqtWelHOiT5qJeOkhHveZOwiiwASG -----END CERTIFICATE----- ``` --- 我们将以下内容部分拷贝下来: ``` -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMQg2pRayMgZD3E8z4iD ev7FpOk5ljyiF3jUNPLcPtX2fnEYQsQTc8af85Mk+Q8jH5icqlkJOs+0KsBH899m AjWNU6yppySq5jklp5vg3TMK+ShO4VRC+/b2aJwqbGYoPHDrgMu7xpl3eaFJiwcj oeNIsPQYTL5UlzROGFjO0CkXaRRq/sRHhJbRL2kBey8i/+gB8GACpgq/GeRREJu0 XzLO5sgSeRG5OyzNeURvCS2D9mtx4o86WDicIjev3hVjubKyhNumILk/ZV4zPQy/ k2T+DnyoapAFg/6xK2NxQdkFEu7/kma9tde6Uj1QDVvzXf89ZWnPSPEzaHVACzZk pwIDAQAB -----END PUBLIC KEY----- ``` ### 保存公钥 新建一个文件,名为:`public.cert`。将内容拷贝进文件中。以备后用。 # 完善角色和权限数据交互 ### 创建RoleMapper接口 ```java package com.gosuncn.dao; @Mapper public interface RoleMapper { List<Role> getRolesByUserId(@Param("userId") Integer userId); } ``` ### 创建RoleMapper.xml文件 ```xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.gosuncn.dao.RoleMapper"> <select id="getRolesByUserId" parameterType="java.lang.Integer" resultType="com.gosuncn.entity.Role"> SELECT `role`.`id`, `role`.`name`, `role`.`desc` FROM `user_role` LEFT JOIN `role` ON `user_role`.`role_id` = `role`.`id` WHERE `user_role`.`user_id` = #{userId} </select> </mapper> ``` ### 创建AccessMapper接口 ```java package com.gosuncn.dao; @Mapper public interface AccessMapper { List<Access> getAccessesByRoleId(@Param("roleId") Integer roleId); } ``` ### 创建AccessMapper.xml文件 ```xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.gosuncn.dao.AccessMapper"> <select id="getAccessesByRoleId" parameterType="java.lang.Integer" resultType="com.gosuncn.entity.Access"> SELECT `id`, `name`, `desc` FROM `role_access` LEFT JOIN `access` ON `role_access`.`access_id` = `access`.`id` WHERE `role_access`.`role_id` = #{roleId} </select> </mapper> ``` ### 创建RoleService类 ```java package com.gosuncn.service; @Service public class RoleService { @Autowired private RoleMapper roleMapper; public Optional<List<Role>> getRolesByUserId(Integer userId) { return Optional.ofNullable(roleMapper.getRolesByUserId(userId)); } } ``` ### 创建AccessService类 ```java package com.gosuncn.service; @Service public class AccessService { @Autowired private AccessMapper accessMapper; public Optional<List<Access>> getAccessesByRoleId(Integer roleId) { return Optional.ofNullable(accessMapper.getAccessesByRoleId(roleId)); } } ``` ### 修改UserController类 ```java package com.gosuncn.controller; @RestController public class UserController { @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private AccessService accessService; @GetMapping("/user") public CommonResult getUserByUsername(@RequestParam String username) { return new CommonResult(HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase(), userService.getUserByUsername(username).orElse(null)); } @GetMapping("/role/userId/{userId}") public CommonResult getRolesByUserId(@PathVariable("userId") Integer userId) { return new CommonResult(HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase(), roleService.getRolesByUserId(userId).orElse(null)); } @GetMapping("/access/roleId/{roleId}") public CommonResult getAccessesByRoleId(@PathVariable("roleId") Integer roleId) { return new CommonResult(HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase(), accessService.getAccessesByRoleId(roleId).orElse(null)); } } ``` ### 启动项目测试 ```http http://127.0.0.1/role/userId/1 ``` ```json {"code":200,"msg":"OK","data":[{"id":1,"name":"ADMIN","desc":"管理员"}]} ``` ```http http://127.0.0.1/access/roleId/1 ``` ```json {"code":200,"msg":"OK","data":[{"id":1,"name":"all","desc":"测试数据"}]} ``` # OAuth2协议防护系统 ### 引入依赖 ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> ``` ##### pom文件完整版 ```xml <?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>cloudframe</artifactId> <groupId>com.gosuncn</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>authorize</artifactId> <dependencies> <dependency> <groupId>com.gosuncn</groupId> <artifactId>common</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>com/gosuncn/dao/*.xml</include> </includes> </resource> </resources> </build> </project> ``` ### 自定义UserDetails > 类名:AuthUserDetails ```java package com.gosuncn.entity; public class AuthUserDetails implements UserDetails { private Integer id; // 用户id private String username; // 用户名 private String password; // 密码 private Boolean enabled; // 是否启用 private List<GrantedAuthority> authorities; // 权限列表 public AuthUserDetails(User user, List<GrantedAuthority> authorities) { this(user.getId(), user.getUsername(), user.getPassword(), user.getEnabled(), authorities); } public AuthUserDetails(Integer id, String username, String password, Boolean enabled, List<GrantedAuthority> authorities) { this.id = id; this.username = username; this.password = password; this.enabled = enabled; this.authorities = authorities; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return this.authorities; } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return this.enabled; } } ``` ### 自定义UserDetailsService > 类名:AuthUserDetailsService ```java package com.gosuncn.service; @Service @AllArgsConstructor public class AuthUserDetailsService implements UserDetailsService { private final static String ROLE_PREFIX = "ROLE_"; private final static String SEPARATED_STRING = ","; @Autowired private UserMapper userMapper; @Autowired private RoleMapper roleMapper; @Autowired private AccessMapper accessMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userMapper.getUserByUsername(username); if (user == null) { return new AuthUserDetails(-1, username, "unknown", false, Collections.EMPTY_LIST); } StringBuilder authorityBuilder = new StringBuilder(); for (Role role : roleMapper.getRolesByUserId(user.getId())) { authorityBuilder.append(ROLE_PREFIX + role.getName()); for (Access access : accessMapper.getAccessesByRoleId(role.getId())) { authorityBuilder.append(SEPARATED_STRING).append(access.getName()); } } return new AuthUserDetails(user, AuthorityUtils.commaSeparatedStringToAuthorityList(authorityBuilder.toString())); } } ``` ### 配置认证成功处理 ```java package com.gosuncn.config; @Component public class AuthSuccessHandler implements AuthenticationSuccessHandler { private static final String CONTENT_TYPE = "application/json; charset=UTF-8"; @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { response.setContentType(CONTENT_TYPE); response.getWriter().write(objectMapper.writeValueAsString(new CommonResult<>(HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase(), authentication))); } } ``` > 将authentication暴露只是测试实验,实际投入生产切不可暴露。 ### 配置认证失败处理 ```java package com.gosuncn.config; @Component public class AuthFailureHandler implements AuthenticationFailureHandler { private static final String CONTENT_TYPE = "application/json; charset=UTF-8"; @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { response.setContentType(CONTENT_TYPE); response.getWriter().write(objectMapper.writeValueAsString(new CommonResult<>(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase(), exception.getMessage()))); } } ``` ### 创建SpringSecurity配置文件 ```java @Configuration @AllArgsConstructor public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final PasswordEncoder passwordEncoder; private final UserDetailsService userDetailsService; private final AuthSuccessHandler successHandler; private final AuthFailureHandler failureHandler; @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .permitAll() .successHandler(successHandler) .failureHandler(failureHandler); http.authorizeRequests() .anyRequest().authenticated(); http.csrf() .disable(); } } ``` ### 配置PasswordEncoder密码编码器 ```java package com.gosuncn.config; @Configuration public class PasswordEncoderConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } ``` ### 配置token存储策略 ##### 创建配置TokenStoreConfig类 ```java package com.gosuncn.config; import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory; @Configuration public class TokenStoreConfig { private static final String DIR_NAME = "PrivateKey" + "/"; @Value("${cloudframe.private-key}") private String privateKey; @Value("${cloudframe.key-password}") private String keyPassword; @Value("${cloudframe.alias}") private String alias; @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setKeyPair(new KeyStoreKeyFactory(new ClassPathResource(DIR_NAME + privateKey), keyPassword.toCharArray()).getKeyPair(alias)); return converter; } } ``` ##### 修改配置文件application.yml ```yaml server: port: 80 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///sys_rbac?serverTimezone=UTC username: root password: root mybatis: mapper-locations: classpath*:/com/gosuncn/dao/*.xml cloudframe: private-key: cigc.jks key-password: cigcjks alias: cigcjks ``` ### 创建授权服务器配置文件 ```java package com.gosuncn.config; import javax.sql.DataSource; @Configuration @AllArgsConstructor @EnableAuthorizationServer public class AuthServerConfig extends AuthorizationServerConfigurerAdapter { private final TokenStore tokenStore; private final AuthenticationManager authenticationManager; private final JwtAccessTokenConverter accessTokenConverter; private final DataSource dataSource; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(new JdbcClientDetailsService(dataSource)); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints .tokenStore(tokenStore) .accessTokenConverter(accessTokenConverter) .authenticationManager(authenticationManager) .allowedTokenEndpointRequestMethods(HttpMethod.POST); } @Override public void configure(AuthorizationServerSecurityConfigurer security) { security .tokenKeyAccess("permitAll()") .allowFormAuthenticationForClients(); } } ```