Jwt 整合 Security 详解笔记
@TOC
1.创建项目、导入依赖
<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
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逆向生成工具类
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();
}
}
- 记得添加扫包注解
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). 用户表
/*
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).角色表
/*
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
/*
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).用户-角色表
/*
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
/*
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类
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 接口
package com.oldbai.sercuityandjwt.util.resultdata;
/**
* 封装API的错误码
* Created by macro on 2019/4/19.
*/
public interface IErrorCode {
long getCode();
String getMessage();
}
ResultCode 枚举类
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.运行截图(顺序已乱......不想调了)