目录
Java学习之Spring Security整合JWT详解学习笔记

Jwt 整合 Security 详解笔记

这篇详解笔记参考的学习链接奉上:
参考学习链接一
参考学习链接二

@TOC

1.创建项目、导入依赖

xml
复制代码
<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>
    <scope>test</scope>
</dependency>

<!--mybatis plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.1.tmp</version>
</dependency>
<!--构造器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.3.1.tmp</version>
</dependency>
<!--逆向生成的模板-->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>
<!--mysql连接工具-->
<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>

<!--        导入 swageer2 和 swagger-ui-->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>3.0.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>3.0.0</version>
</dependency>

<!--SpringSecurity依赖配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--Hutool Java工具包-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>4.5.7</version>
</dependency>
<!--JWT(Json Web Token)登录支持-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

工具说明:

  • Security
  • JWT(Json Web Token)登录支持
  • Swagger UI 3.0
  • MyBatis plus
  • Mybatis plus 逆向工程工具
  • MyBatis plus 逆向工程生成模板
  • Mysql连接工具
  • Hutool Java工具包

2.配制文件 application.yml

yml
复制代码
spring:
  datasource:
    url: jdbc:mysql://192.168.82.41:7777/jwtandsercurity?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
#mybatis-plus:
#  configuration:
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 自定义jwt key
jwt:
  tokenHeader: Authorization #JWT存储的请求头
  secret: mySecret #JWT加解密使用的密钥
  expiration: 604800 #JWT的超期限时间(60*60*24)
  tokenHead: Bearer  #JWT负载中拿到开头

3.MyBatis-plus逆向生成工具类

java
复制代码
package com.oldbai.sercuityandjwt;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.util.ArrayList;
import java.util.List;

/**
 * 代码生成器
 */
public class Main {
    public static void main(String[] args) {
        AutoGenerator autoGenerator = new AutoGenerator();
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setDbType(DbType.MYSQL);
        //数据库连接信息
        dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSourceConfig.setUsername("root");
        dataSourceConfig.setPassword("root");
        dataSourceConfig.setUrl("jdbc:mysql://192.168.82.41:7777/mmall?useUnicode=true&characterEncoding=UTF-8");
        
        autoGenerator.setDataSource(dataSourceConfig);
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOpen(true);
        globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java");
        globalConfig.setAuthor("oldbai");
        globalConfig.setServiceName("%sService");
        autoGenerator.setGlobalConfig(globalConfig);
        PackageConfig packageConfig = new PackageConfig();
        //需要配置
        packageConfig.setParent("com.oldbai.sercuityandjwt");
        
        packageConfig.setEntity("entity");
        packageConfig.setMapper("mapper");
        packageConfig.setController("controller");
        packageConfig.setService("service");
        packageConfig.setServiceImpl("service.impl");
        autoGenerator.setPackageInfo(packageConfig);
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setEntityLombokModel(true);
        strategyConfig.setNaming(NamingStrategy.underline_to_camel);
        strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);

        List<TableFill> list = new ArrayList<>();
        //如果需要生成时间
        TableFill tableFill1 = new TableFill("create_time",FieldFill.INSERT);
        TableFill tableFill2 = new TableFill("update_time",FieldFill.INSERT_UPDATE);
        list.add(tableFill1);
        list.add(tableFill2);

        strategyConfig.setTableFillList(list);
        autoGenerator.setStrategy(strategyConfig);

        autoGenerator.execute();
    }
}
  • 记得添加扫包注解
java
复制代码
package com.oldbai.sercuityandjwt;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.oldbai.sercuityandjwt.mapper")
@SpringBootApplication
public class SercuityandjwtApplication {

    public static void main(String[] args) {
        SpringApplication.run(SercuityandjwtApplication.class, args);
    }

}

4.数据库分析

(1). 用户表

plaintext
复制代码
/*
Navicat MySQL Data Transfer

Source Server         : docker_mysql
Source Server Version : 50732
Source Host           : 192.168.30.132:3306
Source Database       : jwtandsercurity

Target Server Type    : MYSQL
Target Server Version : 50732
File Encoding         : 65001

Date: 2021-01-09 14:11:09
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL COMMENT '账号',
  `password` varchar(255) NOT NULL COMMENT '密码',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '$10$b2PI77rj1ch52aE85.Og7OW5xUfjOppJVw8WdqgfLlCTw7atXb3Lu');
INSERT INTO `sys_user` VALUES ('2', 'abel', '$10$b2PI77rj1ch52aE85.Og7OW5xUfjOppJVw8WdqgfLlCTw7atXb3Lu');
INSERT INTO `sys_user` VALUES ('4', 'oldbai', '$2a$10$/MJkaIDZ0hnZo3imTGAIWOSifiJLbzQL0zrzkJVNvzufK80j43BPK');
INSERT INTO `sys_user` VALUES ('5', 'xiaobai', '$2a$10$NhsRxTeXVFZNotZPSripqOgoOGvkgVcOqg2gwyvReO4JhFNhoMSJu');

字段分析

  • username:用户名
  • password:密码
  • 其他字段可以自行添加

(2).角色表

plaintext
复制代码
/*
Navicat MySQL Data Transfer

Source Server         : docker_mysql
Source Server Version : 50732
Source Host           : 192.168.30.132:3306
Source Database       : jwtandsercurity

Target Server Type    : MYSQL
Target Server Version : 50732
File Encoding         : 65001

Date: 2021-01-09 14:10:53
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL COMMENT '角色名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'ROLE_ADMIN');
INSERT INTO `sys_role` VALUES ('2', 'ROLE_USER');

字段分析

  • name:角色名
  • 其他字段可以自行添加

(3).权限表:sys_permission.sql

plaintext
复制代码
/*
Navicat MySQL Data Transfer

Source Server         : docker_mysql
Source Server Version : 50732
Source Host           : 192.168.30.132:3306
Source Database       : jwtandsercurity

Target Server Type    : MYSQL
Target Server Version : 50732
File Encoding         : 65001

Date: 2021-01-09 14:10:08
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL COMMENT '权限名',
  `descritpion` varchar(255) DEFAULT NULL COMMENT '描述',
  `url` varchar(255) DEFAULT NULL COMMENT '前端资源路径,比如/user/**',
  `pid` int(11) DEFAULT NULL COMMENT '父级权限id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES ('1', 'ROLE_HOME', 'home', '/sysUser/', null);
INSERT INTO `sys_permission` VALUES ('2', 'ROLE_ADMIN', 'Abel', '/sysPermission/', null);

字段分析

  • name : 权限名称,具体是根据该注解进行的。
  • descritpion : 描述该权限的功能
  • url : 描述该权限访问的路径
  • pid : 父级权限的节点(一级权限、二级权限、三级权限,此处不使用)
  • 其他字段可以自行添加

(4).用户-角色表

plaintext
复制代码
/*
Navicat MySQL Data Transfer

Source Server         : docker_mysql
Source Server Version : 50732
Source Host           : 192.168.30.132:3306
Source Database       : jwtandsercurity

Target Server Type    : MYSQL
Target Server Version : 50732
File Encoding         : 65001

Date: 2021-01-09 14:22:54
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for sys_role_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_user`;
CREATE TABLE `sys_role_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `sys_user_id` int(11) DEFAULT NULL,
  `sys_role_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of sys_role_user
-- ----------------------------
INSERT INTO `sys_role_user` VALUES ('1', '1', '1');
INSERT INTO `sys_role_user` VALUES ('2', '2', '2');
INSERT INTO `sys_role_user` VALUES ('3', '4', '1');
INSERT INTO `sys_role_user` VALUES ('4', '5', '2');

字段分析

  • sys_user_id : 用户ID
  • sys_role_id : 角色ID

(5).角色-用户表:sys_permission_role.sql

plaintext
复制代码
/*
Navicat MySQL Data Transfer

Source Server         : docker_mysql
Source Server Version : 50732
Source Host           : 192.168.30.132:3306
Source Database       : jwtandsercurity

Target Server Type    : MYSQL
Target Server Version : 50732
File Encoding         : 65001

Date: 2021-01-09 14:21:53
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for sys_permission_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission_role`;
CREATE TABLE `sys_permission_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL,
  `permission_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of sys_permission_role
-- ----------------------------
INSERT INTO `sys_permission_role` VALUES ('1', '1', '1');
INSERT INTO `sys_permission_role` VALUES ('2', '2', '1');
INSERT INTO `sys_permission_role` VALUES ('3', '1', '2');

字段分析

  • role_id : 角色ID
  • permission_id : 权限ID

(6).表分析

  • 通过用户可以查询该用户所属角色,通过该角色可以得到所拥有的权限。

5. 统一返回结果工具包

com.oldbai.sercuityandjwt.util.resultdata

CommonResult类

java
复制代码
package com.oldbai.sercuityandjwt.util.resultdata;

/**
 * 通用返回对象
 * Created by macro on 2019/4/19.
 */
public class CommonResult<T> {
    private long code;
    private String message;
    private T data;

    protected CommonResult() {
    }

    protected CommonResult(long code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     */
    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
    }

    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     * @param  message 提示信息
     */
    public static <T> CommonResult<T> success(T data, String message) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
    }

    /**
     * 失败返回结果
     * @param errorCode 错误码
     */
    public static <T> CommonResult<T> failed(IErrorCode errorCode) {
        return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
    }

    /**
     * 失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> failed(String message) {
        return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
    }

    /**
     * 失败返回结果
     */
    public static <T> CommonResult<T> failed() {
        return failed(ResultCode.FAILED);
    }

    /**
     * 参数验证失败返回结果
     */
    public static <T> CommonResult<T> validateFailed() {
        return failed(ResultCode.VALIDATE_FAILED);
    }

    /**
     * 参数验证失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> validateFailed(String message) {
        return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
    }

    /**
     * 未登录返回结果
     */
    public static <T> CommonResult<T> unauthorized(T data) {
        return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
    }

    /**
     * 未授权返回结果
     */
    public static <T> CommonResult<T> forbidden(T data) {
        return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
    }

    public long getCode() {
        return code;
    }

    public void setCode(long code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

IErrorCode 接口

java
复制代码
package com.oldbai.sercuityandjwt.util.resultdata;

/**
 * 封装API的错误码
 * Created by macro on 2019/4/19.
 */
public interface IErrorCode {
    long getCode();

    String getMessage();
}

ResultCode 枚举类

java
复制代码
package com.oldbai.sercuityandjwt.util.resultdata;
/**
 * 枚举了一些常用API操作码
 * Created by macro on 2019/4/19.
 */
public enum ResultCode implements IErrorCode {
    SUCCESS(200, "操作成功"),
    FAILED(500, "操作失败"),
    VALIDATE_FAILED(404, "参数检验失败"),
    UNAUTHORIZED(401, "暂未登录或token已经过期"),
    FORBIDDEN(403, "没有相关权限");
    private long code;
    private String message;

    private ResultCode(long code, String message) {
        this.code = code;
        this.message = message;
    }

    public long getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

6.Token工具类

JwtTokenUtil

java
复制代码
package com.oldbai.sercuityandjwt.util.token;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 老白
 */
@Component
public class JwtTokenUtil {

    /**
     * 用户调用登录接口,登录成功后获取到JWT的token;
     * 之后用户每次调用接口都在http的header中添加一个叫Authorization的头,值为JWT的token;
     * 后台程序通过对Authorization头中信息的解码及数字签名校验来获取其中的用户信息,从而实现认证和授权。
     * 相关方法说明:
     * <p>
     * generateToken(UserDetails userDetails) :用于根据登录用户信息生成token
     * getUserNameFromToken(String token):从token中获取登录用户的信息
     * validateToken(String token, UserDetails userDetails):判断token是否还有效
     */

    private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "created";
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     * 根据负责生成JWT的token
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 从token中获取JWT中的负载
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            LOGGER.info("JWT格式验证失败:{}", token);
        }
        return claims;
    }

    /**
     * 生成token的过期时间
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    /**
     * 从token中获取登录用户名
     */
    public String getUserNameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * 验证token是否还有效
     *
     * @param token       客户端传入的token
     * @param userDetails 从数据库中查询出来的用户信息
     */
    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUserNameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    /**
     * 判断token是否已经失效
     */
    private boolean isTokenExpired(String token) {
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(new Date());
    }

    /**
     * 从token中获取过期时间
     */
    private Date getExpiredDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

    /**
     * 根据用户信息生成token
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    /**
     * 判断token是否可以被刷新
     */
    public boolean canRefresh(String token) {
        return !isTokenExpired(token);
    }

    /**
     * 刷新token
     */
    public String refreshToken(String token) {
        Claims claims = getClaimsFromToken(token);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

}

7.Swagger ui 的配置类

swaggerConfig

java
复制代码
package com.oldbai.sercuityandjwt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.List;

/**
 * Swagger ui 的配置类
 *
 * @Api:用在请求的类上,表示对类的说明 tags="说明该类的作用,可以在UI界面上看到的注解"
 * value="该参数没什么意义,在UI界面上也看到,所以不需要配置"
 * @ApiOperation:用在请求的方法上,说明方法的用途、作用 value="说明方法的用途、作用"
 * notes="方法的备注说明"
 * @ApiImplicitParams:用在请求的方法上,表示一组参数说明
 * @ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面 name:参数名
 * value:参数的汉字说明、解释
 * required:参数是否必须传
 * paramType:参数放在哪个地方
 * · header --> 请求参数的获取:@RequestHeader
 * · query --> 请求参数的获取:@RequestParam
 * · path(用于restful接口)--> 请求参数的获取:@PathVariable
 * · body(不常用)
 * · form(不常用)
 * dataType:参数类型,默认String,其它值dataType="Integer"
 * defaultValue:参数的默认值
 * @ApiResponses:用在请求的方法上,表示一组响应
 * @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息 code:数字,例如400
 * message:信息,例如"请求参数没填好"
 * response:抛出异常的类
 * @ApiModel:用于响应类上,表示一个返回响应数据的信息 (这种一般用在post创建的时候,使用@RequestBody这样的场景,
 * 请求参数无法使用@ApiImplicitParam注解进行描述的时候)
 * @ApiModelProperty:用在属性上,描述响应类的属性
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig implements WebMvcConfigurer {
    @Bean
    public Docket createRestApi() {
        ApiInfo apiInfo =
                new ApiInfoBuilder()
                        .title("日志管理系统API")
                        .description("日志管理系统采用SpringBoot开发,API文档集成Swagger")
                        .version("1.0")
                        .contact(new Contact("老白", "www.oldbai.top", "934105499@qq.com"))
                        .license("我的首页")
                        .licenseUrl("https://weibo.com/")
                        .build();
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.oldbai.sercuityandjwt.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.
                addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
                .resourceChain(false);
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/swagger-ui/")
                .setViewName("forward:" + "/swagger-ui/index.html");
    }
}

8.Security 配制类

(1)SecurityConfig

java
复制代码
package com.oldbai.sercuityandjwt.config;

import com.oldbai.sercuityandjwt.util.securityUtil.JwtAuthenticationTokenFilter;
import com.oldbai.sercuityandjwt.util.securityUtil.RestAuthenticationEntryPoint;
import com.oldbai.sercuityandjwt.util.securityUtil.RestfulAccessDeniedHandler;
import com.oldbai.sercuityandjwt.util.securityUtil.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;


/**
 * SpringSecurity的配置
 *
 * @author macro
 * @date 2018/4/26
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Resource
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    @Resource
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf
                .disable()
                .sessionManagement()// 基于token,所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, // 允许对于网站静态资源的无授权访问
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/swagger-resources/**",
                        "/v2/api-docs/**"
                )
                .permitAll()
                .antMatchers("/sysUser/login", "/sysUser/register")// 对登录注册要允许匿名访问
                .permitAll()
//                .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求
//                .permitAll()
//                .antMatchers("/**")//测试时全部运行访问
//                .permitAll()
                .anyRequest()// 除上面外的所有请求全部需要鉴权认证
                .authenticated();
        // 禁用缓存
        httpSecurity.headers().cacheControl();
        // 添加JWT filter
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint);
    }

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



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

    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

(2)Security 需要的工具类

1)AdminUserDetails

java
复制代码
package com.oldbai.sercuityandjwt.util.securityUtil;


import com.oldbai.sercuityandjwt.entity.SysPermission;
import com.oldbai.sercuityandjwt.entity.SysUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * SpringSecurity需要的用户详情
 *
 * @author macro
 * @date 2018/4/26
 */
public class AdminUserDetails implements UserDetails {
    private SysUser umsAdmin;
    private List<SysPermission> permissionList;

    public AdminUserDetails(SysUser umsAdmin, List<SysPermission> permissionList) {
        this.umsAdmin = umsAdmin;
        this.permissionList = permissionList;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        //返回当前用户的权限
//        return permissionList.stream()
//                .filter(permission -> permission.getName() != null)
//                .map(permission -> new SimpleGrantedAuthority(permission.getName()))
//                .collect(Collectors.toList());
//        这个属性中存储了这个用户所有的权限。
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (SysPermission sysPermission : permissionList) {
            authorities.add(new SimpleGrantedAuthority(sysPermission.getName()));
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return umsAdmin.getPassword();
    }

    @Override
    public String getUsername() {
        return umsAdmin.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
/**
 * 是否启用
 * 1 启用
 * 0 不启用
 */
//        return umsAdmin.getStatus().equals(1);

        return true;
    }
}

2)JwtAuthenticationTokenFilter

java
复制代码
package com.oldbai.sercuityandjwt.util.securityUtil;

import com.oldbai.sercuityandjwt.util.token.JwtTokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * JWT登录授权过滤器
 *
 * @author macro
 * @date 2018/4/26
 */
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
    @Resource
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    /**
     * token 拦截器
     * 在用户名和密码校验前添加的过滤器,
     * 如果请求中有jwt的token且有效,
     * 会取出token中的用户名,
     * 然后调用SpringSecurity的API进行登录操作。
     *
     * @param request
     * @param response
     * @param chain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
//        从请求头中获取token ,传来的 token 结构 前置请求头 jwt数据 (中间有个空格)
        String authHeader = request.getHeader(this.tokenHeader);
//        匹配前置请求头是否和后台设置的一样
        if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
            // The part after "Bearer "
//            切割字符串,去除前置请求头,获取jwt的数据
            String authToken = authHeader.substring(this.tokenHead.length());
//            从jwt数据中获取username
            String username = jwtTokenUtil.getUserNameFromToken(authToken);
//            打印日志
            LOGGER.info("checking username:{}", username);
//            判断用户名是否存在,
//            SecurityContextHolder.getContext().getAuthentication() 获取储存用户信息 必定为空,因为用户信息返回给了前端
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
//                通过用户名进行登录
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                //验证token是否还有效
                if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                    //获取用户 ,security里面的用户
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    //设置用户信息
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    LOGGER.info("authenticated user:{}", username);
                    //将该用户存入security中 
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }

        chain.doFilter(request, response);
    }
}

3)RestAuthenticationEntryPoint

java
复制代码
package com.oldbai.sercuityandjwt.util.securityUtil;

import cn.hutool.json.JSONUtil;
import com.oldbai.sercuityandjwt.util.resultdata.CommonResult;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 当未登录或者token失效访问接口时,自定义的返回结果
 * Created by macro on 2018/5/14.
 */
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().println(JSONUtil.parse(CommonResult.unauthorized(authException.getMessage())));
        response.getWriter().flush();
    }
}

4)RestfulAccessDeniedHandler

java
复制代码
package com.oldbai.sercuityandjwt.util.securityUtil;
import cn.hutool.json.JSONUtil;
import com.oldbai.sercuityandjwt.util.resultdata.CommonResult;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 当访问接口没有权限时,自定义的返回结果
 *
 * @author macro
 * @date 2018/4/26
 */
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler{
    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException e) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
        response.getWriter().flush();
    }
}

5)UserDetailsService

java
复制代码
package com.oldbai.sercuityandjwt.util.securityUtil;

import com.oldbai.sercuityandjwt.entity.SysPermission;
import com.oldbai.sercuityandjwt.entity.SysUser;
import com.oldbai.sercuityandjwt.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.List;

public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private SysUserService userService;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        SysUser admin = userService.getAdminByUsername(s);
        if (admin != null) {
            List<SysPermission> permissionList = userService.getPermissionList(admin.getId());
            return new AdminUserDetails(admin, permissionList);
        }
        throw new UsernameNotFoundException("用户名或密码错误");

    }
}

9.其他配置

controller

SysUserController

java
复制代码
package com.oldbai.sercuityandjwt.controller;


import com.oldbai.sercuityandjwt.entity.SysPermission;
import com.oldbai.sercuityandjwt.entity.SysUser;
import com.oldbai.sercuityandjwt.service.SysUserService;
import com.oldbai.sercuityandjwt.util.resultdata.CommonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import org.springframework.stereotype.Controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author oldbai
 * @since 2021-01-09
 */
@RestController
@Api(tags = "UmsAdminController", description = "后台用户管理")
@RequestMapping("/sysUser")
public class SysUserController {
    @Autowired
    private SysUserService userService;
    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @ApiOperation(value = "登录以后返回token")
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult login(@RequestBody SysUser sysUser, BindingResult result) {
        String token = userService.login(sysUser.getUsername(), sysUser.getPassword());
        if (token == null) {
            return CommonResult.validateFailed("用户名或密码错误");
        }
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("token", token);
        tokenMap.put("tokenHead", tokenHead);
        tokenMap.put("jwt",tokenHead+" "+token);
        return CommonResult.success(tokenMap);
    }


    @PreAuthorize("hasAuthority('ROLE_HOME')")
    @GetMapping("/hello")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", name = "Authorization", dataType = "String", required = true, value = "token")
    })
    public String hello() {
        return "hello";
    }



    @ApiOperation(value = "用户注册")
    @RequestMapping(value = "/register", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<SysUser> register(@RequestBody SysUser umsAdminParam, BindingResult result) {
        SysUser sysUser = userService.register(umsAdminParam);
        if (sysUser == null) {
            CommonResult.failed();
        }
        return CommonResult.success(sysUser);
    }


    @ApiOperation("获取用户所有权限(包括+-权限)")
    @RequestMapping(value = "/permission/{adminId}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<SysPermission>> getPermissionList(@PathVariable Long adminId) {
        List<SysPermission> permissionList = userService.getPermissionList(Math.toIntExact(adminId));
        return CommonResult.success(permissionList);
    }
}

  • @PreAuthorize("hasAuthority('ROLE_HOME')") 注解,里面的 ROLE_HOME 字段对应权限表的 name 属性,必须加上这个注解才能实现权限访问。
  • @ApiImplicitParams({
    @ApiImplicitParam(paramType = "header", name = "Authorization", dataType = "String", required = true, value = "token")
    }) ----> 使用swagger ui 的设置 header 携带 jwt 的信息。
  • BindingResult result 可以不要。

SysPermissionController

java
复制代码
package com.oldbai.sercuityandjwt.controller;


import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author oldbai
 * @since 2021-01-09
 */
@RestController
@RequestMapping("/sysPermission")
public class SysPermissionController {

    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", name = "Authorization", dataType = "String", required = true, value = "token")
    })
    @GetMapping("/hello")
    public String hello() {
        return "helloSysper";
    }

}

  • 另一个测试接口。

接口说明:

  • SysUserController 可以访问的角色有:ROLE_ADMIN 、 ROLE_USER
  • sysPermission 可以访问的角色有:ROLE_ADMIN

service

SysUserService

java
复制代码
package com.oldbai.sercuityandjwt.service;

import com.oldbai.sercuityandjwt.entity.SysPermission;
import com.oldbai.sercuityandjwt.entity.SysUser;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;

/**
 * <p>
 * 服务类
 * </p>
 *
 * @author oldbai
 * @since 2021-01-09
 */
public interface SysUserService extends IService<SysUser> {
    /**
     * 通过用户名查找该用户
     *
     * @param username
     * @return
     */
    SysUser getAdminByUsername(String username);

    /**
     * 获取该用户的所有权限
     * <p>
     * 先找到这个用户的角色
     * <p>
     * 通过这个用户的角色获取权限
     *
     * @param id
     * @return
     */
    List<SysPermission> getPermissionList(Integer id);


    /**
     * 登录功能
     *
     * @param username 用户名
     * @param password 密码
     * @return 生成的JWT的token
     */
    String login(String username, String password);


    /**
     * 注册功能
     *
     * @param sysUser
     * @return
     */
    SysUser register(SysUser sysUser);
}

SysUserServiceImpl

java
复制代码
package com.oldbai.sercuityandjwt.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.oldbai.sercuityandjwt.entity.SysPermission;
import com.oldbai.sercuityandjwt.entity.SysPermissionRole;
import com.oldbai.sercuityandjwt.entity.SysRoleUser;
import com.oldbai.sercuityandjwt.entity.SysUser;
import com.oldbai.sercuityandjwt.mapper.SysPermissionMapper;
import com.oldbai.sercuityandjwt.mapper.SysPermissionRoleMapper;
import com.oldbai.sercuityandjwt.mapper.SysRoleUserMapper;
import com.oldbai.sercuityandjwt.mapper.SysUserMapper;
import com.oldbai.sercuityandjwt.service.SysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oldbai.sercuityandjwt.util.token.JwtTokenUtil;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author oldbai
 * @since 2021-01-09
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Resource
    private SysUserMapper mapper;
    @Resource
    private SysRoleUserMapper mapper1;
    @Resource
    private SysPermissionRoleMapper roleMapper;
    @Resource
    private SysPermissionMapper permissionMapper;
    @Resource
    private UserDetailsService userDetailsService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    public SysUser getAdminByUsername(String username) {

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("username", username);
        return mapper.selectOne(wrapper);
    }

    @Override
    public List<SysPermission> getPermissionList(Integer id) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("sys_user_id", id);
        SysRoleUser sysRoleUser = mapper1.selectOne(wrapper);
        wrapper = new QueryWrapper();
        wrapper.eq("role_id", sysRoleUser.getSysRoleId());
        List<SysPermissionRole> selectList = roleMapper.selectList(wrapper);
        List<SysPermission> list = new ArrayList<>();
        for (SysPermissionRole permissionRole : selectList) {
            wrapper = new QueryWrapper();
            wrapper.eq("id", permissionRole.getPermissionId());
            SysPermission role = permissionMapper.selectOne(wrapper);
            list.add(role);
        }
        return list;
    }

    @Override
    public String login(String username, String password) {
        String token = null;
        try {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if (!passwordEncoder.matches(password, userDetails.getPassword())) {
                throw new BadCredentialsException("密码不正确");
            }
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
        } catch (AuthenticationException e) {
//            LOGGER.warn("登录异常:{}", e.getMessage());
            e.printStackTrace();
        }
        return token;
    }

    @Override
    public SysUser register(SysUser sysUser) {
        SysUser user = new SysUser();
//        复制属性
        BeanUtils.copyProperties(sysUser, user);
//        user.setCreateTime(new Date());
//        user.setStatus(1);
        //查询是否有相同用户名的用户
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("username", user.getUsername());
        SysUser one = mapper.selectOne(wrapper);
        if (one != null) {
            return null;
        }
        //将密码进行加密操作
        String encodePassword = passwordEncoder.encode(user.getPassword());
        user.setPassword(encodePassword);
        mapper.insert(user);
        return user;
    }
}

entity

SysPermission

java
复制代码
package com.oldbai.sercuityandjwt.entity;

import java.io.Serializable;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 *
 * </p>
 *
 * @author oldbai
 * @since 2021-01-09
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class SysPermission implements Serializable {

    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 权限名
     */
    private String name;

    /**
     * 权限描述 授权链接
     */
    private String descritpion;

    /**
     * 访问的地址,比如/user/**
     */
    private String url;

    /**
     * 父id 父节点id
     */
    private Integer pid;

}

SysPermissionRole

java
复制代码
package com.oldbai.sercuityandjwt.entity;

import java.io.Serializable;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author oldbai
 * @since 2021-01-09
 */
@Data
  @EqualsAndHashCode(callSuper = false)
  @Accessors(chain = true)
public class SysPermissionRole implements Serializable {

    private static final long serialVersionUID=1L;
    @TableId(value = "id", type = IdType.AUTO)
      private Integer id;

    private Integer roleId;

    private Integer permissionId;


}

SysRole

java
复制代码
package com.oldbai.sercuityandjwt.entity;

import java.io.Serializable;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author oldbai
 * @since 2021-01-09
 */
@Data
  @EqualsAndHashCode(callSuper = false)
  @Accessors(chain = true)
public class SysRole implements Serializable {

    private static final long serialVersionUID=1L;
  @TableId(value = "id", type = IdType.AUTO)
      private Integer id;

      /**
     * 角色名称
     */
      private String name;


}

SysUser

java
复制代码
package com.oldbai.sercuityandjwt.entity;

import java.io.Serializable;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
 * <p>
 *
 * </p>
 *
 * @author oldbai
 * @since 2021-01-09
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class SysUser implements Serializable {

    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 账号
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    public SysUser(String username, String password) {
        this.username = username;
        this.password = password;
    }
}

SysRoleUser

java
复制代码
package com.oldbai.sercuityandjwt.entity;

import java.io.Serializable;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author oldbai
 * @since 2021-01-09
 */
@Data
  @EqualsAndHashCode(callSuper = false)
  @Accessors(chain = true)
public class SysRoleUser implements Serializable {

    private static final long serialVersionUID=1L;
    @TableId(value = "id", type = IdType.AUTO)
      private Integer id;

    private Integer sysUserId;

    private Integer sysRoleId;


}

10.运行截图(顺序已乱......不想调了)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文由 oldbai 原创发布于 阳光沙滩 , 未经作者授权,禁止转载
评论
0 / 1024
用户头像
爱了爱了
2021-11-15 06:29回复
用户头像
详细得离谱
2021-02-24 01:40回复
推荐文章
linux服务器的日志、容器清理
本手册提供了一套全面的Linux服务器磁盘清理与排查方法,帮助用户高效管理磁盘空间。通过查看磁盘使用情况、分析Docker占用、清理无用容器和日志,确保系统运行流畅。适合需要优化服务器性能的运维人员参考。
记给网站接入图形验证码
本文分享了如何为个人站点添加图形验证码以提升安全性,详细介绍了使用第三方接口实现验证码功能的过程,并通过代码示例展示了具体实现方式。对于关注网站安全的开发者来说,这篇文章提供了实用的参考价值。
Java开发环境搭建
本文是一份详细的Java开发环境搭建指南,涵盖了Java的发展历史、版本选择及安装配置过程。无论你是初学者还是有经验的开发者,都能从中找到实用的信息和步骤,帮助你快速上手Java开发。
Vue3.js项目的创建
本文详细介绍了如何使用Vue3创建项目,包括Node.js的安装和配置,以及项目的初始化过程。适合初学者学习如何快速搭建Vue3开发环境,并推荐了相关课程供进一步学习。
Network Tools : 您的运维“助手”升级了
Network Tools是一款专为解决运维难题而设计的软件,集设备管理、配置备份、性能监控和告警处理于一体。通过统一的工作台,帮助运维人员更高效地管理资产、定位故障并提升团队协作效率。无论是设备在线状态还是具体问题排查,都能得到全面支持。
Ubuntu 软件安装与彻底卸载指南
本文详细介绍了在 Ubuntu 系统中如何规范安装和彻底卸载 .deb 格式软件,包括依赖修复、残留清理以及图形化工具推荐,适合需要维护系统环境的用户。
JavaWeb开发客户程序更新特殊环境下的骚操作
本文分享了在无外网环境下,如何利用Docker和docker-compose实现高效部署的实战经验。通过基础镜像和本地jar包挂载的方式,解决了内网环境下的依赖问题,适合需要灵活部署方案的技术人员参考。
Linux 防火墙完全指南:从原理到多发行版实战
本文全面解析Linux防火墙的底层原理与主流工具配置,涵盖UFW、firewalld、iptables、nftables等方案,适合不同发行版用户。从基础概念到生产环境加固建议,助你掌握核心安全策略,确保服务器与网络的安全运行。
水上派对英语单词篇
探索FLOATOPIA水上派对的完整指南,涵盖签到流程、场地指引、安全须知、活动安排及夜场舞会等实用英语词汇与对话。无论是初学者还是老手,都能轻松掌握关键术语,享受完美水上体验。
麒麟操作系统下使用 virt-customize 重置虚拟机 root 密码实战
本文详细介绍了在麒麟操作系统环境下使用 virt-customize 工具重置 KVM 虚拟机 root 密码的完整流程,包含安装步骤、常见问题及解决方案,帮助运维人员高效处理密码遗忘或批量初始化场景,避免数据丢失。
浅析JS预编译-函数篇
本文详细讲解了JavaScript中函数的预编译过程,包括AO对象的创建、变量和函数声明的提升,以及执行阶段的变化。适合初学者了解JS执行机制,帮助深入理解代码运行逻辑。
访问Github的另一种姿势
本文介绍了如何通过Watt ToolKit等工具加速访问Github,适合对网络技术感兴趣的读者。内容涵盖工具介绍、下载和使用方法,以及一些实用技巧,值得一看。
使用Gulp压缩JS
本文详细介绍了如何使用Gulp压缩JavaScript文件,特别是支持ES6语法的处理方法。通过实际操作和效果对比,帮助开发者提升代码性能,适合对前端自动化工具感兴趣的读者。
使用Gulp压缩CSS
本文详细介绍了如何使用Gulp工具对CSS文件进行压缩,提升网页加载速度。适合需要优化静态资源的开发者,提供清晰的步骤和示例,帮助快速上手。
从0部署Nuxtjs项目
本文详细介绍了如何将Nuxt.js项目部署到公网,涵盖Node.js安装、环境配置、源码上传及使用PM2启动服务的全过程,适合开发者学习和实践。
Linux 命令行美化利器:tree 命令完全指南(安装 + 全部参数详解)
掌握 Linux 下的 `tree` 命令,快速直观地查看目录结构。本文详细讲解安装方法、参数含义及实战场景,适合开发者和系统管理员提升工作效率。
Linux常用命令(一)
本文详细介绍了Linux基础命令,包括文件操作、系统显示、网络状态、软件包管理等,适合初学者学习和查阅,是掌握Linux系统的实用指南。
大模型推理参数解码:温度、Top-p 与核采样如何塑造生成文本的灵魂
探索大语言模型的采样参数如何塑造生成内容,从温度到Top-p,从惩罚机制到熵控制,揭示背后的信息论原理与实践策略。了解如何通过参数调优,让AI既保持知识的严谨,又拥有创造力的自由。
记从0使用Claude code写代码
本文介绍了如何使用Claude Code搭配DeepSeek进行编程,详细讲解了安装Node.js、全局安装Claude、配置模型以及使用方法。对于开发者来说,这是一份实用的技术教程,尤其适合对AI编程工具感兴趣的读者。
Android-Studio-Gradle同步失败-Library为null的通用排查指南
遇到 Android Studio Gradle Sync 失败时,不要轻易删除全局缓存。本文详细解析了错误原因,并提供了一系列精准的排查步骤和解决方案,帮助开发者快速定位并解决问题,提升开发效率。
PHP实现密码加密
本文详细介绍了Bcrypt密码加密技术及其在PHP中的应用,帮助开发者提升用户密码的安全性。通过实际代码示例,展示了如何使用password_hash和password_verify函数进行密码加密与验证,是学习网络安全知识的实用指南。
WordCloud效果,滚动标签
本文详细介绍了如何在Vue项目中集成WordCloud组件,包括依赖安装、属性配置和使用方法。适合开发者学习如何实现数据可视化功能,提升项目交互体验。
从0使用WordPress搭建一个优美的网站
本文详细介绍了如何使用WordPress搭建一个美观的网站,从安装到主题配置和功能拓展,为读者提供了实用的操作指南。无论是初学者还是有一定经验的开发者,都能从中获得有价值的参考。
JS实现在网站底部添加运行时间
想知道如何在网站底部显示运行时间?本文详细讲解了通过JavaScript实现这一功能的方法,包括时间计算逻辑和代码实现。适合前端开发者学习参考,轻松为网站添加实用功能。
在Vercel上部署Hexo博客
本文详细介绍了如何在Vercel上快速部署Hexo博客,无需后端服务即可实现高效发布。相比传统方式,省去了手动生成和上传静态文件的步骤,更加便捷。适合想要搭建个人博客的开发者参考。
从0搭建一个Hexo博客
本文详细介绍了Hexo博客框架的使用方法,从安装到部署全流程讲解,适合想快速搭建个人博客的技术爱好者。内容清晰易懂,是入门Hexo的理想指南。
离线设备激活方案:古老的 Windows 光盘激活,离线算法授权等
本文深入解析了离线激活的核心原理与实现方式,从历史案例到现代技术,全面剖析了如何在无网络环境下确保软件授权的安全性。通过设备指纹、授权文件校验等手段,为开发者提供了可复用的架构设计思路,适用于工业、医疗等对网络依赖较低的场景。
Hexo实现生成站点地图
想为你的Hexo博客添加站点地图功能吗?本文详细介绍了如何通过安装插件和配置文件来实现,适合没有内置该功能的新主题或自定义主题的用户。简单步骤助你提升搜索引擎优化效果。
Hexo实现代码压缩
本文分享了如何通过Hexo插件优化博客性能,详细介绍了安装和配置过程,帮助提升网页加载速度。适合对网站优化感兴趣的开发者阅读。
记一次 GitHub 幽灵协作者大清洗:强制重写 Git 历史与穿透 CDN 缓存实践
本文详细讲解了如何解决GitHub上出现的‘幽灵协作者’问题,通过重写Git历史和穿透CDN缓存,彻底清理错误提交记录。适合开发者学习如何高效管理项目历史与优化仓库信息。