文章目录
一、前言
做web开发,离不开一个工作,那就是鉴权。
所谓鉴权,通常包含2重含义。一则是对接口访问的权限控制,二则是对接口数据的获取控制。
1.1 鉴权-接口权限管理
所谓接口权限的访问控制,指的是接口对哪些人开放。比如,一些广告内容,就需要对所有人开放,无论该用户是否登录。一些操作,比如修改收获地址,必须登录后才能修改。还有一些操作,需要超级管理员才能做,比如禁言某些用户。
1.2 鉴权-数据权限管理
所谓数据权限管理,是指同一接口,不同人访问到的数据是不一样的。比如查看用设备列表,只能查看到用户所在部门和其子孙部门的设备。再比如修改用户信息,假定接口写的是:
UserEntity edit(Long pId, UserInfoDto pUserInfoDto);
对于非管理员用户,只有操作pId=自己的id时,才是有效的。或者,用户只能修改自己和其子孙部门节点下的用户数据。
1.3 鉴权-ext-数据有效性校验
除了上面说的那些,广义的鉴权还包括数据有效性的校验。比如密码必须达到一定的强度,一些参数需要有值域等。这样一来防止用户互操作导致数据错乱,二来防止别有用心者攻击系统。
除了有效性校验,还有一个重要的目标,就是把账号、密码等敏感信息加密,防止不法分子截包。同时,我们希望使用注解方式,不要在业务逻辑中体现出这一点,让代码更加干净。
1.4 本章目标
鉴权部分看着简单,但实际编写起来代码量很大。我打算分3个章节来介绍。本章聚焦于最基础的工作,登录和注册。后面章节会讲解接口权限管理和数据权限管理,最后一章讲数据有效性校验。
二、难点分析程序设计
2.1 鉴权方式介绍
2.1.1 sesson鉴权
用户登录后,如何确定用户已登录呢?在单体时代,通常用session的方式。当用户登录成功后,就生成了一个httpsesson对象。而后在session对象中,设置上登录成功信息。每次有消息访问时,通过拦截器判断是否session中有登录信息,如果没有则拦截。
在微服务时代,客户端的消息会首先通过网关,然后再转发给对应的微服务。并且同一网站的不同接口,可能在不同的服务器中。这时,就需要引入redis-session来处理,让session信息存储在redis里。
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
这种方式有个巨大的优势,就是几乎不需要前端做任何额外配合,就可以实现登录。当然,更智能的还可以让前端把鉴权信息写入cookie,服务器的拦截器在拦截到相关信息后,直接把session设置为登录状态,这样连登录也可以自动了。
但这种方式仍有弊端。如果一个网站的接口是多个系统组成的,不同系统可能是不同的开发商,他们的服务器没有接在统一的网关和服务注册发现上;异或网关服务器也做了集群,这时session的鉴权方式就又不好使了。另一方面,这种方式每访问一次接口,都需要进行一次redis的访问,如果接口不需要做IO运算(虽说信息系统很少这样的),那也会感觉亏亏的。
2.1.2 基于redis的短token
用户登录后,生成一个uuid或其他什么的什么id,以此为键在redis中存一个登录信息,我们把这个key成为短token。而后把这个id返回给前端。前端每次发送请求后,携带上这个短token。拦截器发现这个短token后,从redist中取出信息,设置到httpAttribute上,可供业务服务直接查询相应的信息。
这个做法即使多个网关也不会有问题,而且可以做登录状态的踢掉功能。
但这个做法的一个弊端是,想使用这个系统的鉴权,必须接入这个系统的redis。每次请求,都必须经过一次redis的访问。比如在大量设备的物联网系统,设备每5秒发送一次信息,这样每次都要查redis,也是一笔很大的消耗。
2.1.3 基于JWT的长token
JWT规定了数据传输的结构,一串完整的JWT由三段落组成,每个段落用英文句号连接(.)连接,他们分别是:Header、Payload、Signature,所以,常规的JWT内容格式是这样的:AAA.BBB.CCC。并且,这串内容会经过base64编码。
AAA部分是头信息,包含了加密方式、类型和失效时间;BBB部分是体信息,存放了想存放的数据,比如用户Id,用户名,角色,部门等;CCC部分是签名;签名的生成方式只有服务器知道,每个JWT文对应一个签名,如果JWT的BBB发生更改,签名则无法匹配,也无法得出相应的签名。
这样一来,事情就变得有趣起来。我们只需要在用户登陆成功后,生成相应的JWT返回给前端。前端只需要在报文头中携带这个token信息,拦截器拿到该信息,验证签名,就可以认为该客户端完成了登录。解析BBB的内容,就可以获得必要的用户信息。在这个过程中,服务端没有和数据库做任何的交互,大大提升了程序的运行效率。同时也使得各微服务不依赖于特定数据库,在QQ登录的账号信息,在腾讯视频也可以使用,并且腾讯视频的服务器不需要去对接QQ的数据库。真的很赛艇,很有科技感呢。
但这种方式也有弊端,第一个就是无法做踢掉操作。毕竟签名一旦给了前端,在到期前就永久生效,很难优雅的禁止用户登录。当然,不优雅的话,在redis存下用户的信息,每次请求再经过一遍redis,但如果这样做,那直接使用2.1.2的方案就行了,何必引入JWT呢。
第二个问题是,如果把失效时间设置的很短,就需要用户频繁登录,会比较麻烦。如果把时间设置的太长,如果JWT信息被别有用心者截取,别有用心这就可以在有效期内登录用户的账号。这个问题的解决,可以在2.1.4得到缓解。
第三个弊端是,理论上base64是很容易反解的,也就是说可以通过截包,知道很多用户的信息。但其实就算知道了,别有用心者到底能干什么呢?如果实在介意,可以修改下加密算法,比如更换下base64的字符顺序,或者在次之前再做一次加密。
2.1.4 长短双token模式
如果系统对鉴权的要求比较高的话,可以把2.1.2和2.1.3的方式结合起来使用。用户成功登录后,生成存储在redis的短token,客户端可以使用短token向服务端申请长token。这样,长token就可以把失效时间设置的短一些,比如5分钟。
这样一来,长token的安全性就大大提升。同时,通常的请求也不需要访问redis,大大提升了效率。
本次攻略,打算采取2.1.3的方式,只要原理理解,小伙伴们想自己实现下2.1.4也很容易。
2.1.5 常见的鉴权库
常见的第三方库有shiro和springsecurity。shiro使用是一个老的三方库,使用的是2.1.2 基于redis的短token数据。不过shiro使用起来十分方便。springsecurity定义了一系列的接口,可以使用JWT的鉴权方式。不过springsecurity的接入十分麻烦,学习成本很高。可能折腾半天,会发现springsecurity并没有做什么工作。
本节攻略不打算使用第三方鉴权库,从拦截器开始写,纯手工打造鉴权系统。
2.2 密码加密方式
互联网上,已经传出了很多数据库数据被泄露,导致用户密码被泄露,通过撞库的方法,盗取其他平台的账号。(所谓撞库,大部分用户在各平台的账号密码都是相同的,一个地方的数据库泄露,就可以去其他平台盗号)
传统的做法,是MD5加盐加密。在数据库中,除了存储密码的密文,还存放一个盐。密文 = MD5(密码+盐)。在校验密码时,取出数据库中的盐值,和用户输入的密码一起再加密,和数据库中的密文做对比。相同则验证通过。
然而,我们都知道,现在MD5是可以被反解的,这就很尴尬。同时,还在数据库中多增加了一个字段。
我比较推荐的是springsecurity中spring-security-crypto。这个算法不需要随机盐,而是自动随机。该算法经过多次迭代,每次加密后得到的密文都是不同的。但有一个match接口,可以得出密文是否是特定密码生成的。并且,从密文推导出明文,则显得很困难。
2.3 数据设计
为明确我们的目标,我们先假定一个业务场景,做一下数据库设计。
本场景打算实现用户的登录注册。同时实现部门管理和角色管理。业务上做一个设备管理系统。
2.3.1 powerdesinger截图
2.3.2 sql文
略,看着2.3.1的图自己画一个,可以生成
2.3.3 视图
CREATE
ALGORITHM = UNDEFINED
DEFINER = `dbMgr`@`%`
SQL SECURITY DEFINER
VIEW `view_user_with_role` AS
SELECT
`sys_user`.`id` AS `id`,
`sys_user`.`name` AS `name`,
`sys_rel_user_role`.`role_id` AS `role_id`,
`sys_rel_user_role`.`role_code` AS `role_code`,
`sys_user`.`nick_name` AS `nick_name`,
`sys_user`.`description` AS `description`,
`sys_user`.`email` AS `email`,
`sys_user`.`phone` AS `phone`,
`sys_user`.`gender` AS `gender`,
`sys_user`.`birthday` AS `birthday`,
`sys_user`.`dep_code` AS `dep_code`,
`sys_user`.`dep_cascade_code` AS `dep_cascade_code`,
`sys_user`.`ban` AS `ban`,
`sys_user`.`expire_time` AS `expire_time`
FROM
(`sys_user`
JOIN `sys_rel_user_role` ON ((`sys_user`.`id` = `sys_rel_user_role`.`user_id`)))
2.4 代码复用性设计
在许多系统中,都会用到鉴权相关的操作。在大公司,会搭建一个鉴权中台。鉴权中台是一个微服务,所有鉴权操作在该中台完成。
由于我们是攻略性质的帖子,不打算做那么复杂。但也要考虑复用性这个问题。所以,我们打算做成一个模块,供各种项目的鉴权微服务引用。
2.4.1 基础实体类
由于业务的需求各种各样,我们不知道具体的业务中,用户数据会存放哪些信息,用户-角色表会冗余哪些信息。所以我们在设计基础模块时,不能把实体定死。但我们可以先定义好想实现鉴权操作所必要的字段:
package indi.zhifa.recipe.bailan.framework.auth.entity.po;
@Data
@TableName("sys_user")
@Schema(title = "BaseUserEntity对象", description = "用户表")
public class BaseUserEntity extends BaseEntity {
@Schema(title = "用户名")
protected String name;
@Schema(title = "密码")
protected String password;
@Schema(title = "过期时间")
protected LocalDateTime expireTime;
@Schema(title = "所属部门")
protected String depCode;
@Schema(title = "所属部门级联")
protected String depCascadeCode;
@Schema(title = "是否被禁用")
protected Boolean ban;
}
2.4.2 DAO层
由于不知道具体业务的实体类到底是什么,mybatis-plus对于实体继承实现的并不好。而且我们想在这一节中,引入缓存操作,加速系统。放基类实现容易造成缓存管理混乱。
于是,我们只能在模块中定义下来,让子模块去实现。
package indi.zhifa.recipe.bailan.framework.auth.dao;
public interface IUserRelevantDao {
BaseUserEntity user_generate();
BaseUserEntity user_check(Long id);
Long user_findByUserName(String pUserName);
default BaseUserEntity user_check(String pUserName){
Long id = user_findByUserName(pUserName);
if(null == id){
throw new ServiceException("没有找到名为"+pUserName+"的用户");
}
return user_check(id);
}
default boolean user_existByName(String pUserName){
Long id = user_findByUserName(pUserName);
return null != id;
}
BaseUserEntity user_save(BaseUserEntity pBaseUserEntity);
BaseUserEntity user_edit(Long pId, BaseUserEntity pBaseUserEntity);
BaseUserEntity user_removeById(Long pId);
List<BaseRoleEntity> role_list();
Long role_findByCode(String pCode);
BaseRoleEntity role_check(Long pId);
default BaseRoleEntity role_check(String pCode){
Long id = role_findByCode(pCode);
if(null == id){
throw new ServiceException("不存在code为"+pCode+"的角色");
}
return role_check(id);
}
default boolean role_existByCode(String pCode){
Long id = role_findByCode(pCode);
return null != id;
}
BaseRoleEntity role_save(BaseRoleEntity pBaseRoleEntity);
BaseRelUserRoleEntity relUserRole_generate();
List<BaseRelUserRoleEntity> relUserRole_listByUserId(Long pUserId);
boolean relUserRole_updateRoles(Long pUserId, List<BaseRelUserRoleEntity> relUserRoleEntityList);
BaseUserEntity user_load(JSONObject pUserCfg);
BaseRoleEntity role_load(JSONObject pRoleCfg);
}
2.4.3 IBaseUserService
我们思考一下用户服务都要实现哪些功能,这里提供接口:
public interface IBaseUserService {
/**
* 初始化,检测数据库是否有默认数据,如果没有则创建
*/
void init();
/**************用户行为************/
/**
* 创建用户
*
* @param pBaseUserEntity 用户实体
* @param pRoles 初始角色,如果填空则分配配置文件中的默认角色
* @return
*/
BaseUserEntity createUser(BaseUserEntity pBaseUserEntity, List<String> pRoles);
/**
* 修改密码
*
* @param pChangePasswdDto 修改密码配置
* @return
*/
BaseUserEntity changePasswdByUser(ChangePasswdDto pChangePasswdDto);
/**************管理员行为************/
/**
* 重置用户密码
*
* @param pUserId 用户Id
* @return
*/
BaseUserEntity resetPasswd(Long pUserId);
/**
* 设置用户禁用状态
*
* @param pUserId 用户Id
* @param pActive 禁用状态
* @return
*/
BaseUserEntity setActive(Long pUserId, boolean pActive);
/**
* 删除用户
*
* @param pUserId 用户Id
* @return
*/
boolean removeUser(Long pUserId);
}
2.4.4 IBaseLoginService
public interface IBaseLoginService {
/**
* 登录
*
* @param pLoginDto 登录信息
* @return
*/
BaseLoginInfoVo login(LoginDto pLoginDto);
}
2.5 接口设计
我们确定一下这次工作的范围,先把controller给出
2.5.1 UserApi
package indi.zhifa.recipe.bailan.busy.auth.controller.api;
@Api(tags = "UserApi-用户接口")
@RequestMapping("/api/user")
@Slf4j
@ZfRestController
@RequiredArgsConstructor
public class UserApi {
private final IUserService mUserService;
@UnLogin
@Operation(summary = "注册")
@PostMapping(value = "/signin")
UserEntity signIn(
@Parameter(description = "注册信息") @RequestBody SignInDto pSignInDto){
UserEntity userEntity = mUserService.signIn(pSignInDto);
return userEntity;
}
@Operation(summary = "修改信息")
@PutMapping(value = "/{id}")
UserEntity edit(
@Parameter(description = "用户Id") @PathVariable(name = "id") Long pId,
@Parameter(description = "用户信息") @RequestBody EditUserDto pEditUserDto){
UserEntity userEntity = mUserService.edit(pId,pEditUserDto);
return userEntity;
}
@PasswordExpireAuth
@Operation(summary = "修改密码")
@PutMapping(value = "/passwd")
String changePasswdByUser(
@Parameter(description = "密码信息") @RequestBody ChangePasswdDto pChangePasswdCfg
){
BaseUserEntity baseUserEntity = mUserService.changePasswdByUser(pChangePasswdCfg);
return "密码修改成功";
}
@Operation(summary = "重置密码")
@PutMapping(value = "/{id}/resetPasswd")
String changePasswdByUser(
@Parameter(description = "用户Id") @PathVariable(name = "id") Long pId){
BaseUserEntity baseUserEntity = mUserService.resetPasswd(pId);
return "密码修改成功";
}
@Operation(summary = "查看信息")
@GetMapping(value = "/{id}")
UserVo info(
@Parameter(description = "用户Id") @PathVariable(name = "id") Long pId){
UserVo userVo = mUserService.info(pId);
return userVo;
}
@Operation(summary = "查看分页")
@GetMapping(value = "/page")
Page<UserVo> page(
@Parameter(description = "当前页",required = true) @RequestParam(name = "current") Integer pCurrent,
@Parameter(description = "页大小",required = true) @RequestParam(name = "size") Integer pSize,
@Parameter(description = "权限码") @RequestParam(name = "roleCode", required = false) String pRoleCode,
@Parameter(description = "用户名") @RequestParam(name = "userName", required = false) String pUserName,
@Parameter(description = "邮箱") @RequestParam(name = "email", required = false) String pEmail,
@Parameter(description = "电话") @RequestParam(name = "phone", required = false) String pPhone,
@Parameter(description = "昵称") @RequestParam(name = "nickName", required = false) String pNickName,
@Parameter(description = "性别") @RequestParam(name = "gender", required = false) Gender pGender,
@Parameter(description = "最小年龄") @RequestParam(name = "ageMin", required = false) Integer pAgeMin,
@Parameter(description = "最大年龄") @RequestParam(name = "ageMax", required = false) Integer pAgeMax
){
Page<UserVo> pageInfo = mUserService.page(pCurrent,pSize,pRoleCode,pUserName,pEmail,pPhone,pNickName,pGender,pAgeMin,pAgeMax);
return pageInfo;
}
@Operation(summary = "删除用户数据")
@DeleteMapping(value = "/{id}")
String delete(@Parameter(description = "用户Id") @PathVariable(name = "id") Long pId){
mUserService.removeUser(pId);
return "删除成功";
}
}
2.5.2 LoginAip
package indi.zhifa.recipe.bailan.busy.auth.controller.api;
@Api(tags = "LoginAip-登录接口")
@RequestMapping("/api/login")
@Slf4j
@ZfRestController
@RequiredArgsConstructor
public class LoginAip {
private final ILoginService mLoginService;
@UnLogin
@PostMapping
BaseLoginInfoVo login(
@Parameter(description = "登录信息") @RequestBody LoginDto pLoginDto){
BaseLoginInfoVo baseLoginInfoVo = mLoginService.login(pLoginDto);
return baseLoginInfoVo;
}
}
三、代码实现
3.1 framework-auth
3.1.1 pom引用
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>bailan</artifactId>
<groupId>indi.zhifa.recipe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>framework-auth</artifactId>
<dependencies>
<dependency>
<groupId>indi.zhifa.recipe</groupId>
<artifactId>framework-web-all</artifactId>
</dependency>
<dependency>
<groupId>bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>140</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>indi.zhifa.recipe</groupId>
<artifactId>framework-enums-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
3.1.2 代码结构截图
3.1.3 entity包
3.1.3.1 annotation
首先,在framework-common中添加UnLogin,用于其他模块的一些接口直接放行登录鉴权
package indi.zhifa.recipe.bailan.framework.common.entity.annotations;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UnLogin {
}
PasswordExpireAuth,用于修改密码接口,跳过密码过期校验
package indi.zhifa.recipe.bailan.framework.auth.entity.annotations;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PasswordExpireAuth {
}
3.1.3.2 dto
BaseRoleDto,用于创建角色
package indi.zhifa.recipe.bailan.framework.auth.entity.dto.user;
@Schema(title = "用户角色基类Dto")
@Data
public class BaseRoleDto {
@Schema(title = "角色码")
String code;
@Schema(title = "角色名")
String name;
@Schema(title = "描述")
String description;
}
BaseSignInDto,用于角色注册
package indi.zhifa.recipe.bailan.framework.auth.entity.dto.user;
@Data
public class BaseSignInDto {
@Schema(title = "用户名")
private String name;
@Schema(title = "密码")
private String password;
}
ChangePasswdDto,用于用户修改密码
package indi.zhifa.recipe.bailan.framework.auth.entity.dto.user;
@Data
public class ChangePasswdDto {
String orgPasswd;
String newPasswd;
}
LoginDto,用于用户登录
package indi.zhifa.recipe.bailan.framework.auth.entity.dto.user;
@Data
public class LoginDto {
@Schema(title = "角色名")
String userName;
@Schema(title = "密码")
String passwd;
}
SysBaseRoleDto 用于创建角色
package indi.zhifa.recipe.bailan.framework.auth.entity.dto;
@Schema(title = "用户角色基类Dto")
@Data
public class SysBaseRoleDto {
@Schema(title = "角色码")
String code;
@Schema(title = "角色名")
String name;
@Schema(title = "描述")
String description;
}
3.1.3.4 BaseTokenObject
该类用于定义基础的BaseTokenObject
package indi.zhifa.recipe.bailan.framework.auth.entity.dto;
@Data
public abstract class BaseTokenObject {
/**
* 用户Id
*/
@Schema(name = "用户Id")
private Long id;
/**
* 用户命
*/
@Schema(name = "用户账号")
private String name;
/**
* ip
*/
@Schema(name = "ip")
private String remoteIp;
/**
* 部门码
*/
@Schema(name = "部门码")
private String depCode;
/**
* 部门码
*/
@Schema(name = "部门级联码")
private String depCascadeCode;
/**
* 密码过期时间
*/
@Schema(name = "密码过期时间")
private LocalDateTime passwdExpireTime;
/**
* 角色
*/
@Schema(name = "角色")
private List<String> roles;
}
3.1.3.5 AuthException
用于鉴权不通过的异常
public class AuthException extends ServiceException {
public AuthException(String pMsg){
super(401,pMsg);
}
}
3.1.3.6 po
BaseUserEntity
见2.4.1 基础实体类
BaseRoleEntity
角色的基础类
package indi.zhifa.recipe.bailan.framework.auth.entity.po;
@Data
@TableName("sys_role")
@Schema(title = "BaseRoleEntity对象", description = "角色表")
public class BaseRoleEntity extends BaseEntity {
@Schema(title = "角色码")
protected String code;
@Schema(title = "角色中文名")
protected String name;
@Schema(title = "角色描述")
protected String description;
}
BaseRelUserRoleEntity
用户和角色关联的基础表
package indi.zhifa.recipe.bailan.framework.auth.entity.po;
@Data
@TableName("sys_rel_user_role")
@Schema(title = "BaseRelUserRoleEntity对象", description = "角色关联表")
public class BaseRelUserRoleEntity extends SysBaseEntity {
@Schema(title = "用户Id")
protected Long userId;
@Schema(title = "用户名")
protected String userName;
@Schema(title = "角色Id")
protected Long roleId;
@Schema(title = "角色code")
protected String roleCode;
}
3.1.3.7 vo
BaseLoginInfoVo
基础登录信息
package indi.zhifa.recipe.bailan.framework.auth.entity.vo;
@Data
public class BaseLoginInfoVo{
@Schema(title = "用户数据")
protected BaseUserEntity userEntity;
@Schema(title = "角色")
protected List<String> roles;
protected String token;
}
RoleVo
角色VO
package indi.zhifa.recipe.bailan.framework.auth.entity.vo;
@Data
public class RoleVo {
@Schema(name = "角色Id")
protected Long roleId;
@Schema(name = "角色Code")
protected String roleCode;
@Schema(name = "角色名字")
protected String roleName;
}
3.1.4 property包
这个包里放着基础的配置类,用于鉴权相关的基础配置
SecurityConfig,鉴权配置的根类
package indi.zhifa.recipe.bailan.framework.auth.property;
@Configuration
@Data
@ConfigurationProperties(prefix = "security")
public class SecurityConfig {
TokenConfig token;
PasswdConfig passwd;
UserConfig userConfig;
}
TokenConfig,token相关配置
@Data
public class TokenConfig {
/**
* token 解析后放在request域中的键
*/
String requestKey;
/**
* JWT相关配置
*/
JWTConfig jwtConfig;
}
JWTConfig,JWT相关配置
@Data
public class JWTConfig {
/**
* token 的秘钥
*/
String secret;
/**
* 持续时长
*/
Integer duration;
/**
* token 的前缀
*/
String prefix;
/**
* 客户端传给服务器token字段的key
*/
String clientTokenKey;
}
PasswdConfig 密码相关配置
@Data
public class PasswdConfig {
/**
* 加密强度
*/
Integer strength;
/**
* 默认密码
*/
String defaultPasswd;
/**
* 过期时间
*/
Long expireDay;
/**
* 客户端传输的账号名密码是否加密
*/
boolean encrypt;
}
UserConfig
用户新创建相关配置
@Data
public class UserConfig {
/**
* 默认部门码
*/
String defaultDepCode;
/**
* 默认部门级联码
*/
String defaultDepCascadeCode;
/**
* 默认角色
*/
List<String> defaultRole;
}
VerifyCodeConfig,验证码相关配置
@Data
public class VerifyCodeConfig {
Integer expire;
}
3.1.5 util包
IBaseTokenUtil token的工具类接口
package indi.zhifa.recipe.bailan.framework.auth.util;
public interface IBaseTokenUtil {
/**
* new 一个TokenObject
*
* @return
*/
BaseTokenObject createTokenObject();
/**
* 通过TokenObject创建一个token串
*
* @param pTokenObject token对象
* @return
*/
String generateToken(BaseTokenObject pTokenObject);
/**
* 把tokenObject放入HttpAttribute域中
*
* @param pTokenObject token对象
* @return
*/
BaseTokenObject putTokenIntoAttribute(BaseTokenObject pTokenObject);
/**
* 该请求是否有token对象
*/
boolean hasTokenObject();
/**
* 获取token对象
*
* @return
*/
BaseTokenObject getTokenObject();
/**
* 获取token对象并转换为子类
*
* @param pTokenClass 子类类型
* @return
* @param <T> token对象
*/
<T extends BaseTokenObject> T getTokenObject(Class<T> pTokenClass);
/**
* 把token串解析成token对象
*
* @param pToken token串
* @return token对象
*/
BaseTokenObject parseToken(String pToken);
}
BaseTokenUtilImpl 实现类
package indi.zhifa.recipe.bailan.framework.auth.util.impl;
public abstract class BaseTokenUtilImpl implements IBaseTokenUtil {
protected final SecurityConfig mSecurityConfig;
protected final Algorithm algorithm;
protected final JWTVerifier verifier;
public BaseTokenUtilImpl(SecurityConfig pSecurityConfig){
mSecurityConfig = pSecurityConfig;
algorithm = Algorithm.HMAC256(mSecurityConfig.getToken().getJwtConfig().getSecret());
verifier = JWT.require(algorithm).build();
}
public String generateToken(BaseTokenObject pTokenObject){
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(
mSecurityConfig.getToken().getJwtConfig().getDuration() * 60);
Date dateExpireTime = LocalDateTimeUtil.localDateTimeToDate(expireTime);
String token = JWT.create()
.withSubject(JSON.toJSONString(pTokenObject))
.withExpiresAt(dateExpireTime)
.sign(algorithm);
String prefix = mSecurityConfig.getToken().getJwtConfig().getPrefix();
return prefix+token;
}
@Override
public BaseTokenObject putTokenIntoAttribute(BaseTokenObject pTokenObject) {
HttpServletRequest request = ZFHttpUtil.getServletRequest();
String requestKey = mSecurityConfig.getToken().getRequestKey();
request.setAttribute(requestKey,pTokenObject);
return pTokenObject;
}
public boolean hasTokenObject(){
HttpServletRequest request;
try{
request = ZFHttpUtil.getServletRequest();
}catch (Exception ex){
request = null;
}
if(null == request){
return false;
}
String requestKey = mSecurityConfig.getToken().getRequestKey();
Object objTokenObject = request.getAttribute(requestKey);
return null != objTokenObject;
}
/**
* 获取token对象
*
* @return
*/
public abstract BaseTokenObject getTokenObject();
public <T extends BaseTokenObject> T getTokenObject(Class<T> pTokenClass) throws ServiceException {
HttpServletRequest request = ZFHttpUtil.getServletRequest();
String requestKey = mSecurityConfig.getToken().getRequestKey();
Object objTokenObject = request.getAttribute(requestKey);
if(null == objTokenObject){
throw new AuthException("还没有设置token");
}
if(!pTokenClass.isInstance(objTokenObject)){
throw new ServiceException("tokenObject 无法转换为 "+pTokenClass.getName());
}
return (T) objTokenObject;
}
/**
* 解析token
*
* @param pToken token字符串
* @return
*/
public abstract BaseTokenObject parseToken(String pToken);
protected <T extends BaseTokenObject> T parseToken(String pToken, Class<T> pTokenClass){
try{
DecodedJWT decodedJWT = JWT.decode(pToken);
try{
decodedJWT = verifier.verify(decodedJWT);
}catch (JWTVerificationException jwtVerificationException){
throw new AuthException("签名不合法");
}
Date expireTimeDate = decodedJWT.getExpiresAt();
LocalDateTime expireTime = LocalDateTimeUtil.dateToLocalDateTime(expireTimeDate);
if(LocalDateTime.now().isAfter(expireTime)){
throw new AuthException("token过期");
}
String subject = decodedJWT.getSubject();
T tokenObject = JSON.parseObject(subject,pTokenClass);
return tokenObject;
}
catch (ServiceException ex){
throw new AuthException("解析token时发生错误,错误信息是: "+ex.getMsg());
}
catch (Exception ex){
throw new AuthException("解析token时发生错误,错误信息是: "+ex);
}
}
}
3.1.6 handler包
3.1.6.1 DefaultMetaObjectHandler
之前的BaseMetaObjectHandlerAdapter,相当于实现了一个空的BaseMetaObjectHandlerAdapter,因为之前没有接入鉴权,没有token,许多审计字段无法填写。现在好了,我们可以补全一个完整版的字段填充Handler
package indi.zhifa.recipe.bailan.framework.auth.handler;
@Slf4j
public abstract class DefaultMetaObjectHandler extends BaseMetaObjectHandler {
private final IBaseTokenUtil mTokenUtil;
protected DefaultMetaObjectHandler(IBaseTokenUtil pTokenUtil){
mTokenUtil = pTokenUtil;
}
@Override
protected void insertCreateBy(MetaObject pMetaObject){
BaseTokenObject tokenObject = mTokenUtil.getTokenObject();
if(pMetaObject.hasSetter(createByStr)){
Object orgCreateBy = pMetaObject.getValue(createByStr);
if(null == orgCreateBy){
final Long id;
if(null != tokenObject){
id = tokenObject.getId();
}else{
id = SYS_ID;
}
this.strictInsertFill(pMetaObject,createByStr,()->id,Long.class);
}
}
}
@Override
protected void insertCreateByName(MetaObject pMetaObject){
BaseTokenObject tokenObject = mTokenUtil.getTokenObject();
if(pMetaObject.hasSetter(createByNameStr)){
Object createByName = pMetaObject.getValue(createByNameStr);
if(null == createByName){
final String name;
if(null != tokenObject){
name = tokenObject.getName();
}else{
name = SYS_NAME;
}
this.strictInsertFill(pMetaObject, createByNameStr,()->name,String.class);
}
}
}
@Override
protected void insertCreateByIp(MetaObject pMetaObject){
BaseTokenObject tokenObject = mTokenUtil.getTokenObject();
if(pMetaObject.hasSetter(createIpStr)){
Object createIp = pMetaObject.getValue(createIpStr);
if(null == createIp){
final String ip;
if(null != tokenObject){
ip = tokenObject.getRemoteIp();
}else{
ip = SYS_IP;
}
this.strictInsertFill(pMetaObject,createIpStr,()->ip,String.class);
}
}
}
@Override
protected void updateModifyBy(MetaObject pMetaObject){
BaseTokenObject tokenObject = mTokenUtil.getTokenObject();
if(pMetaObject.hasSetter(modifyByStr)){
Object orgUpdateBy = pMetaObject.getValue(modifyByStr);
if(null == orgUpdateBy){
final Long id;
if(null != tokenObject){
id = tokenObject.getId();
}else{
id = SYS_ID;
}
this.strictUpdateFill(pMetaObject,modifyByStr,()->id,Long.class);
}
}
}
@Override
protected void updateModifyByName(MetaObject pMetaObject){
BaseTokenObject tokenObject = mTokenUtil.getTokenObject();
if(pMetaObject.hasSetter(modifyByNameStr)){
Object updateByName = pMetaObject.getValue(modifyByNameStr);
if(null == updateByName){
final String name;
if(null != tokenObject){
name = tokenObject.getName();
}else{
name = SYS_NAME;
}
this.strictUpdateFill(pMetaObject, modifyByNameStr,()->name,String.class);
}
}
}
@Override
protected void updateModifyByIp(MetaObject pMetaObject){
BaseTokenObject tokenObject = mTokenUtil.getTokenObject();
if(pMetaObject.hasSetter(modifyIpStr)){
Object updateIp = pMetaObject.getValue(modifyIpStr);
if(null == updateIp){
final String ip;
if(null != tokenObject){
ip = tokenObject.getRemoteIp();
}else{
ip = SYS_IP;
}
this.strictUpdateFill(pMetaObject,modifyIpStr,()->ip,String.class);
}
}
}
@Override
protected boolean hasTokenObject(){
return mTokenUtil.hasTokenObject();
}
}
3.1.6.2 PasswdEncoderFactory
密码加密器的工厂类
@Configuration
@AllArgsConstructor
public class PasswdEncoderFactory {
final SecurityConfig securityConfig;
@Bean
PasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder(securityConfig.getPasswd().getStrength());
}
}
3.1.7 service包
在2.4.3和2.4.4中已经展示过service包的接口,这里只展示实现
BaseUserServiceImpl 用户服务实现基类
package indi.zhifa.recipe.bailan.framework.auth.service.impl;
@Slf4j
@RequiredArgsConstructor
public abstract class BaseUserServiceImpl implements IBaseUserService {
protected final IUserRelevantDao mUserRelevantDao;
protected final SecurityConfig mSecurityConfig;
protected final PasswordEncoder mPasswordEncoder;
protected final IBaseTokenUtil mBaseTokenUtil;
protected LocalDateTime getExpireTime() {
PasswdConfig passwdConfig = mSecurityConfig.getPasswd();
return LocalDateTime.now().plusDays(passwdConfig.getExpireDay());
}
@Transactional(rollbackFor = Exception.class)
@Override
public BaseUserEntity createUser(BaseUserEntity pBaseUserEntity, List<String> pRoles) {
if (mUserRelevantDao.user_existByName(pBaseUserEntity.getName())) {
throw new ServiceException("已经存在名为" + pBaseUserEntity.getName() + "的用户");
}
PasswdConfig passwdConfig = mSecurityConfig.getPasswd();
UserConfig userConfig = mSecurityConfig.getUserConfig();
BaseUserEntity userEntity = pBaseUserEntity;
userEntity.setDepCode(userConfig.getDefaultDepCode());
userEntity.setDepCascadeCode(userConfig.getDefaultDepCascadeCode());
if (StringUtils.hasText(pBaseUserEntity.getPassword())) {
userEntity.setPassword(mPasswordEncoder.encode(pBaseUserEntity.getPassword()));
userEntity.setExpireTime(getExpireTime());
} else {
userEntity.setPassword(mPasswordEncoder.encode(passwdConfig.getDefaultPasswd()));
userEntity.setExpireTime(LocalDateTime.now());
}
userEntity.setBan(false);
userEntity = mUserRelevantDao.user_save(userEntity);
// 处理初始的用户角色
List<BaseRelUserRoleEntity> baseRelUserRoleEntityList = new ArrayList<>();
if (CollectionUtils.isEmpty(pRoles)) {
pRoles = userConfig.getDefaultRole();
}
for (String roleCode : pRoles) {
BaseRelUserRoleEntity baseRelUserRoleEntity = mUserRelevantDao.relUserRole_generate();
baseRelUserRoleEntity.createInit();
baseRelUserRoleEntity.setUserId(userEntity.getId());
baseRelUserRoleEntity.setUserName(userEntity.getName());
BaseRoleEntity baseRoleEntity = mUserRelevantDao.role_check(roleCode);
baseRelUserRoleEntity.setRoleId(baseRoleEntity.getId());
baseRelUserRoleEntity.setRoleCode(baseRoleEntity.getCode());
baseRelUserRoleEntityList.add(baseRelUserRoleEntity);
}
mUserRelevantDao.relUserRole_updateRoles(userEntity.getId(), baseRelUserRoleEntityList);
return userEntity;
}
@Transactional(rollbackFor = Exception.class)
@Override
public BaseUserEntity changePasswdByUser(ChangePasswdDto pChangePasswdDto) {
BaseTokenObject baseTokenObject = mBaseTokenUtil.getTokenObject();
Long userId = baseTokenObject.getId();
BaseUserEntity baseUserEntity = mUserRelevantDao.user_check(userId);
if (!mPasswordEncoder.matches(pChangePasswdDto.getOrgPasswd(),baseUserEntity.getPassword())) {
throw new ServiceException("密码不正确");
}
String newPasswd = mPasswordEncoder.encode(pChangePasswdDto.getNewPasswd());
BaseUserEntity editingUserEntity = mUserRelevantDao.user_generate();
editingUserEntity.updateInit();
editingUserEntity.setId(baseUserEntity.getId());
editingUserEntity.setPassword(newPasswd);
editingUserEntity.setExpireTime(getExpireTime());
mUserRelevantDao.user_edit(editingUserEntity.getId(), editingUserEntity);
return baseUserEntity;
}
@Transactional(rollbackFor = Exception.class)
@Override
public BaseUserEntity resetPasswd(Long pUserId) {
BaseUserEntity userEntity = mUserRelevantDao.user_check(pUserId);
PasswdConfig passwdConfig = mSecurityConfig.getPasswd();
String encodePasswd = mPasswordEncoder.encode(passwdConfig.getDefaultPasswd());
BaseUserEntity editingUserEntity = mUserRelevantDao.user_generate();
editingUserEntity.updateInit();
editingUserEntity.setId(pUserId);
editingUserEntity.setPassword(encodePasswd);
editingUserEntity.setExpireTime(LocalDateTime.now());
mUserRelevantDao.user_edit(editingUserEntity.getId(), editingUserEntity);
return userEntity;
}
@Transactional(rollbackFor = Exception.class)
@Override
public BaseUserEntity setActive(Long pUserId, boolean pActive) {
BaseUserEntity userEntity = mUserRelevantDao.user_check(pUserId);
BaseUserEntity editingUserEntity = new BaseUserEntity();
editingUserEntity.updateInit();
editingUserEntity.setBan(!pActive);
mUserRelevantDao.user_edit(editingUserEntity.getId(), editingUserEntity);
return userEntity;
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean removeUser(Long pUserId) {
mUserRelevantDao.user_removeById(pUserId);
return true;
}
protected List<String> getUserRoles(Long pUserId) {
List<BaseRelUserRoleEntity> roleCodes = mUserRelevantDao.relUserRole_listByUserId(pUserId);
if (CollectionUtils.isEmpty(roleCodes)) {
return new ArrayList<>();
}
return roleCodes.stream().map(BaseRelUserRoleEntity::getRoleCode).collect(Collectors.toList());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void init() {
String pwd= "admin";
ClassPathResource classPathResource = new ClassPathResource("auth_init.json");
byte[] configData = null;
try {
InputStream inputStream = classPathResource.getInputStream();
configData = inputStream.readAllBytes();
} catch (Exception ex) {
log.error("缺失配置文件 " + "auth_init.json");
return;
}
JSONObject authInitConfig = JSON.parseObject(configData);
JSONArray defaultRoles = authInitConfig.getJSONArray("defaultRole");
JSONArray defaultSpMgr = authInitConfig.getJSONArray("defaultSpMgr");
for (int i = 0; i < defaultRoles.size(); i++) {
JSONObject roleJsonCfg = defaultRoles.getJSONObject(i);
BaseRoleEntity baseRoleEntity = mUserRelevantDao.role_load(roleJsonCfg);
if (!mUserRelevantDao.role_existByCode(baseRoleEntity.getCode())) {
mUserRelevantDao.role_save(baseRoleEntity);
}
}
for (int i = 0; i < defaultSpMgr.size(); i++) {
JSONObject userJsonCfg = defaultSpMgr.getJSONObject(i);
List<String> roles = userJsonCfg.getList("roles", String.class);
BaseUserEntity baseUserEntity = mUserRelevantDao.user_load(userJsonCfg);
if (!mUserRelevantDao.user_existByName(baseUserEntity.getName())) {
createUser(baseUserEntity, roles);
}
}
}
}
BaseLoginServiceImpl 登录服务实现基类
package indi.zhifa.recipe.bailan.framework.auth.service.impl;
@RequiredArgsConstructor
public abstract class BaseLoginServiceImpl implements IBaseLoginService {
protected final IUserRelevantDao mUserRelevantDao;
protected final SecurityConfig mSecurityConfig;
protected final PasswordEncoder mPasswordEncoder;
protected final IBaseTokenUtil mBaseTokenUtil;
@Override
public BaseLoginInfoVo login(LoginDto pLoginDto) {
// 对比密码
BaseUserEntity userEntity = mUserRelevantDao.user_check(pLoginDto.getUserName());
if(userEntity.getBan()){
throw new AuthException("账号"+userEntity.getName()+"已被禁用,请联系管理员");
}
String sourceEncodedPasswd = userEntity.getPassword();
if(!mPasswordEncoder.matches(pLoginDto.getPasswd(),sourceEncodedPasswd)){
throw new ServiceException("密码不正确");
}
// 创建BaseTokenObject
BaseTokenObject baseTokenObject = mBaseTokenUtil.createTokenObject();
// 填充用户相关信息
baseTokenObject.setId(userEntity.getId());
baseTokenObject.setName(userEntity.getName());
baseTokenObject.setPasswdExpireTime(userEntity.getExpireTime());
baseTokenObject.setDepCode(userEntity.getDepCode());
baseTokenObject.setDepCascadeCode(userEntity.getDepCascadeCode());
fillOtherInfo(baseTokenObject,userEntity);
// 填充角色相关信息
List<String> roles = getUserRoles(userEntity.getId());
baseTokenObject.setRoles(roles);
// 产生token串
String tokenStr = mBaseTokenUtil.generateToken(baseTokenObject);
// 返回BaseLoginInfoVo
BaseLoginInfoVo baseLoginInfoVo = new BaseLoginInfoVo();
baseLoginInfoVo.setUserEntity(userEntity);
baseLoginInfoVo.setRoles(roles);
baseLoginInfoVo.setToken(tokenStr);
return baseLoginInfoVo;
}
protected abstract BaseTokenObject fillOtherInfo(BaseTokenObject pBaseTokenObject,BaseUserEntity pBaseUserEntity);
protected List<String> getUserRoles(Long pUserId){
List<BaseRelUserRoleEntity> relUserRoleEntityList = mUserRelevantDao.relUserRole_listByUserId(pUserId);
List<String> roles = relUserRoleEntityList.stream().map(BaseRelUserRoleEntity::getRoleCode).collect(Collectors.toList());
return roles;
}
}
3.1.8 filter包
这个包存放拦截器,是鉴权的核心代码
BaseInterceptor,拦截器的基类,这个类可以放到framework-web-common里
package indi.zhifa.recipe.bailan.framework.web.common.filter;
@Slf4j
public class BaseInterceptor {
protected <T extends Annotation> boolean hasAnnotations(Object pHandler, Class<T> cls){
if(pHandler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) pHandler;
// 获取方法上的注解
T requiredPermission = handlerMethod.getMethod().getAnnotation(cls);
if(null != requiredPermission){
return true;
}else{
return false;
}
}
return false;
}
protected void outputError(HttpServletResponse response, String pErrMsg) {
outputError(response, HttpStatus.INTERNAL_SERVER_ERROR.value(),pErrMsg);
}
protected void outputError(HttpServletResponse response, Integer pCode, String pErrMsg) {
PrintWriter writer = null;
try {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
writer = response.getWriter();
RestResponse<String> restResponse = RestResponse.error(pCode,pErrMsg);
writer.print(JSON.toJSONString(restResponse));
} catch (Exception ex) {
log.debug("输出错误时发生故障", ex);
}
}
}
LoginInterceptor 登录核心拦截器
@Component
@Slf4j
@RequiredArgsConstructor
public class LoginInterceptor extends BaseInterceptor implements HandlerInterceptor {
final IBaseTokenUtil mTokenUtil;
final SecurityConfig mSecurityConfig;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws AuthException {
if(hasAnnotations(handler, UnLogin.class)){
return true;
}
TokenConfig tokenConfig = mSecurityConfig.getToken();
String clientTokenKey = tokenConfig.getJwtConfig().getClientTokenKey();
String token = request.getHeader(clientTokenKey);
if(!StringUtils.hasText(token)){
token = request.getParameter(clientTokenKey);
if(!StringUtils.hasLength(token)){
outputError(response,HttpStatus.UNAUTHORIZED.value(),"token 为空,请登录!");
return false;
}
}
String tokenPrefix = tokenConfig.getJwtConfig().getPrefix();
int prefixIdx = token.indexOf(tokenPrefix);
if(prefixIdx == 0){
token = token.substring(tokenPrefix.length());
}
BaseTokenObject tokenObject = parseToken(request,response,handler,token);
if(null == tokenObject){
return false;
}
if(LocalDateTime.now().isAfter(tokenObject.getPasswdExpireTime())){
if(hasAnnotations(handler, PasswordExpireAuth.class)){
return true;
}else{
outputError(response,HttpStatus.UNAUTHORIZED.value(),"密码过期");
return false;
}
}else{
return true;
}
}
protected BaseTokenObject parseToken(HttpServletRequest request, HttpServletResponse response, Object handler, String pToken){
try{
BaseTokenObject tokenObject = mTokenUtil.parseToken(pToken);
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(null != servletRequestAttributes){
String ip = ZFHttpUtil.getRemoteIp();
tokenObject.setRemoteIp(ip);
}
mTokenUtil.putTokenIntoAttribute(tokenObject);
return tokenObject;
}
catch (AuthException ex){
outputError(response,ex.getCode(),ex.getMsg());
return null;
}
catch (ServiceException ex){
outputError(response,ex.getCode(),ex.getMsg());
return null;
}
catch (Exception ex){
outputError(response,ex.getMessage());
return null;
}
}
}
3.2 业务服务
3.2.1 代码结构截图
3.2.2 配置相关
3.2.2.1 pom引用
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>busy</artifactId>
<groupId>indi.zhifa.recipe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>busy-auth</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>indi.zhifa.recipe</groupId>
<artifactId>framework-auth</artifactId>
</dependency>
<dependency>
<groupId>indi.zhifa.recipe</groupId>
<artifactId>framework-enums-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
3.2.2.2 bootstrap.yml
启动yml
server:
# 服务端口
port: 8082
spring:
application:
name: "bailan4-auth"
servlet:
multipart:
enabled : true
max-file-size: "256MB"
max-request-size: "256MB"
cloud:
nacos:
server-addr: localhost:8848
config:
namespace: bailan
name: auth
file-extension: yml
group: bailan4
discovery:
register-enabled: true
3.2.2.3 nacos配置
auth bailan4
spring:
application:
name: bailan3-enums-mgr
datasource:
#数据库配置
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://localhost:3306/auth-test?useUnicode=true&characterEncoding=utf-8
username: app
password: ILv0404@1314
hikari:
# 连接池最大连接数,默认是10
maximum-pool-size: 100
# 最小空闲链接
minimum-idle: 5
# 空闲连接存活最大时间,默认 600000(10分钟)
idle-timeout: 600000
# 数据库连接超时时间,默认30秒,即30000
connection-timeout: 30000
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟;正在使用的连接永远不会退休,只有在关闭后才会被删除。
max-lifetime: 1800000
# 此属性控制从池返回的连接的默认自动提交行为,默认值:true
auto-commit: true
pool-name: Hikari
redis:
host: localhost
port: 6379
password: ilv0404@1314
# 连接超时时间(记得添加单位,Duration)
timeout: 10000ms
lettuce:
shutdown-timeout: 100 # 关闭超时时间
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
max-idle: 8 # 连接池中的最大空闲连接
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
min-idle: 0 # 连接池中的最小空闲连接
swagger:
enable: true
group-name: "用户・登录接口"
api-package: indi.zhifa.recipe.bailan.framework.enums.controller.api;indi.zhifa.recipe.bailan.busy.auth.controller.api;indi.zhifa.recipe.bailan.framework.controller.api
api-regex: "/api/**"
title: "用户・登录接口"
description: "用户・登录接口"
version: "1.0.0"
name: "芝法酱"
email: "hataksumo@163.com"
url: "https://github.com/hataksumo"
enum-memo:
enum-packages:
- indi.zhifa.recipe.bailan.busy.auth.entity.enums
mybatis-plus:
type-enums-package: indi.zhifa.recipe.bailan.busy.auth.entity.enums
security:
token:
request-key: "token"
jwt-config:
secret: "guess@0404"
duration: 120
prefix: "bearer:"
client-token-key: "auth"
passwd:
strength: 4
default-passwd: jihaChyann@kawaii
expire-day: 365
encrypt: false
user-config:
default-dep-code: rt
default-dep-cascade-code: rt
default-role:
- guest
redis:
cache-expire: 120
3.2.2.4 auth_init.json
默认超级管理员账号配置
{
"defaultRole": [
{
"code": "guest",
"name": "客人",
"description": "客人可以浏览公开信息"
},
{
"code": "sysMgr",
"name": "系统管理员",
"description": "可以设计产品,设计规则"
},
{
"code": "staff",
"name": "员工",
"description": "可以管理设备"
},
{
"code": "accountMgr",
"name": "账户管理员",
"description": "账户管理员,可以"
}
],
"defaultSpMgr": [
{
"name": "admin",
"password": "admin",
"email": "admin@163.com",
"phone": "10086",
"nickName": "超级管理员",
"description": "超级管理员",
"birthday": "1949-10-01",
"gender": "MALE",
"roles": ["guest","sysMgr","staff","accountMgr"]
}
]
}
3.2.3 启动类
package indi.zhifa.recipe.bailan.busy.auth;
@EnableCaching
@MapperScan(basePackages ={"indi.zhifa.recipe.bailan.framework.**.mapper","indi.zhifa.recipe.bailan.busy.auth.dao.mapper"})
@SpringBootApplication(
scanBasePackages = {"indi.zhifa.recipe.bailan.framework","indi.zhifa.recipe.bailan.busy.auth"}
)
public class AuthApplication {
public static void main(String[] args){
ApplicationContext context = SpringApplication.run(AuthApplication.class, args);
}
}
3.2.4 启服回调
Init
package indi.zhifa.recipe.bailan.busy.auth.controller.init;
@RequiredArgsConstructor
@Component
public class Init implements CommandLineRunner {
private final IEnumMemoService mEnumMemoService;
private final EnumMemoConfig mEnumMemoConfig;
private final IUserService mUserService;
@Override
public void run(String... args) throws Exception {
DtoEntityUtil.init();
mEnumMemoService.initEnum(mEnumMemoConfig.getEnumPackages());
mUserService.init();
}
}
3.2.5 entity包
3.2.5.1 dto
EditUserDto 编辑用户
package indi.zhifa.recipe.bailan.busy.auth.entity.dto;
@Data
public class EditUserDto {
@Schema(title = "昵称")
private String nickName;
@Schema(title = "自我描述")
private String description;
@Schema(title = "性别")
private Gender gender;
@Schema(title = "生日")
private LocalDate birthday;
}
SignInDto 注册dto
@Data
public class SignInDto extends BaseSignInDto {
@Schema(title = "昵称")
private String nickName;
@Schema(title = "自我描述")
private String description;
@Schema(title = "邮箱地址")
private String email;
@Schema(title = "电话号码")
private String phone;
@Schema(title = "性别")
private Gender gender;
@Schema(title = "生日")
private LocalDate birthday;
}
TokenObject
@Data
public class TokenObject extends BaseTokenObject {
String nickName;
}
3.2.5.2 enums
package indi.zhifa.recipe.bailan.busy.auth.entity.enums;
@RequiredArgsConstructor
@EnumDesc(name = "性别",desc = "性别描述",defaultIdx = 0)
public enum Gender {
DEFAULT(0,"保密","性别神马的都是浮云"),
MALE(1,"男","勇敢帅气的男士"),
FEMALE(2,"女","美丽可爱的女士");
@EnumValue
@Getter
final int code;
@Getter
final String name;
@Getter
final String description;
}
3.2.5.3 po
UserEntity
package indi.zhifa.recipe.bailan.busy.auth.entity.po;
@Data
@TableName("sys_user")
@Schema(title = "BaseUserEntity对象", description = "用户表")
public class UserEntity extends BaseUserEntity {
@Schema(title = "昵称")
private String nickName;
@Schema(title = "自我描述")
private String description;
@Schema(title = "邮箱地址")
private String email;
@Schema(title = "电话号码")
private String phone;
@Schema(title = "性别")
private Gender gender;
@Schema(title = "生日")
private LocalDate birthday;
}
RoleEntity
@Data
@TableName("sys_role")
@Schema(title = "RoleEntity对象", description = "角色表")
public class RoleEntity extends BaseRoleEntity {
}
RelUserRoleEntity
@Data
@TableName("sys_rel_user_role")
@Schema(title = "RelUserRoleEntity对象", description = "角色关联表")
public class RelUserRoleEntity extends BaseRelUserRoleEntity {
}
UserWithRoleEntity
@TableName("view_user_with_role")
@Data
public class UserWithRoleEntity{
@Schema(title = "主键")
@TableId(type = IdType.ASSIGN_ID)
protected Long id;
@Schema(title = "昵称")
private String nickName;
@Schema(title = "自我描述")
private String description;
@Schema(title = "邮箱地址")
private String email;
@Schema(title = "电话号码")
private String phone;
@Schema(title = "用户名")
protected String name;
@Schema(title = "所属部门")
protected String depCode;
@Schema(title = "所属部门级联")
protected String depCascadeCode;
@Schema(title = "角色码")
protected String roleCode;
@Schema(title = "角色Id")
protected String roleId;
@Schema(title = "性别")
private Gender gender;
@Schema(title = "生日")
private LocalDate birthday;
}
3.2.5.4 vo
package indi.zhifa.recipe.bailan.busy.auth.entity.vo;
@Data
public class UserVo extends UserEntity {
List<String> roles;
}
3.2.6 dao层
3.2.6.1 mapper
代码放一起方便展示
package indi.zhifa.recipe.bailan.busy.auth.dao.mapper;
public interface RelUserRoleMapper extends BaseMapper<RelUserRoleEntity> {
}
public interface RoleMapper extends BaseMapper<RoleEntity> {
}
public interface UserMapper extends BaseMapper<UserEntity> {
}
public interface UserWithRoleMapper extends BaseMapper<UserWithRoleEntity> {
}
3.2.6.2 dbService
3.2.6.2.1 接口
IUserDbService
package indi.zhifa.recipe.bailan.busy.auth.dao.service;
public interface IUserDbService extends IZfDbService<UserEntity> {
Long findByUserName(String pUserName);
Long findByEmail(String pEmail);
Long findByPhone(String pPhone);
}
IRoleDbService
public interface IRoleDbService extends IZfDbService<RoleEntity> {
Long findByCode(String pCode);
}
IRelUserRoleDbService
public interface IRelUserRoleDbService extends IZfDbService<RelUserRoleEntity> {
List<RelUserRoleEntity> listByUserId(Long pUserId);
List<RelUserRoleEntity> edit(Long pUserId, List<RelUserRoleEntity> relRoles);
boolean deleteByUserId(Long pUserId);
}
IUserWithRoleDbService
public interface IUserWithRoleDbService extends IZfDbService<UserWithRoleEntity> {
}
3.2.6.2.2 实现
这里的实现,注意我对缓存的操作
UserDbServiceImpl
package indi.zhifa.recipe.bailan.busy.auth.dao.service.impl;
@CacheConfig(cacheNames = "sys_user")
@Service
public class UserDbServiceImpl extends ZfDbServiceImpl<UserMapper, UserEntity> implements IUserDbService {
@Cacheable(key = "#pId")
@Override
public UserEntity check(Serializable pId){
return super.check(pId);
}
@CachePut(key = "#pId")
@Override
public UserEntity savePull(Long pId, UserEntity pEntity) {
return super.savePull(pId,pEntity);
}
@CachePut(key = "#pId")
@Override
public UserEntity updatePull(Long pId, UserEntity pEntity){
return super.updatePull(pId,pEntity);
}
@CacheEvict(key = "#pId")
public UserEntity deleteById(Serializable pId){
return super.deleteById(pId);
}
@CachePut(value = "sys_user-byName", key = "#pUserName")
@Override
public Long findByUserName(String pUserName) {
LambdaQueryWrapper<UserEntity> queryWrapper = Wrappers.<UserEntity>lambdaQuery()
.eq(UserEntity::getName,pUserName);
UserEntity userEntity = findOne(queryWrapper);
if(null == userEntity){
return null;
}
return userEntity.getId();
}
@CachePut(value = "sys_user-byEmail", key = "#pEmail")
@Override
public Long findByEmail(String pEmail) {
LambdaQueryWrapper<UserEntity> queryWrapper = Wrappers.<UserEntity>lambdaQuery()
.eq(UserEntity::getEmail,pEmail);
UserEntity userEntity = findOne(queryWrapper);
if(null == userEntity){
return null;
}
return userEntity.getId();
}
@CachePut(value = "sys_user-byPhone", key = "#pPhone")
@Override
public Long findByPhone(String pPhone) {
LambdaQueryWrapper<UserEntity> queryWrapper = Wrappers.<UserEntity>lambdaQuery()
.eq(UserEntity::getPhone,pPhone);
UserEntity userEntity = findOne(queryWrapper);
if(null == userEntity){
return null;
}
return userEntity.getId();
}
}
RoleDbServiceImpl
@RequiredArgsConstructor
@CacheConfig(cacheNames = "sys_role")
@Service
public class RoleDbServiceImpl extends ZfDbServiceImpl<RoleMapper, RoleEntity> implements IRoleDbService {
private final RedisUtil mRedisUtil;
@Cacheable(value = "sys_role-byCode",key = "#pCode")
@Override
public Long findByCode(String pCode) {
LambdaQueryWrapper<RoleEntity> queryWrapper = Wrappers.<RoleEntity>lambdaQuery()
.eq(RoleEntity::getCode,pCode);
RoleEntity roleEntity = findOne(queryWrapper);
if(null != roleEntity){
return roleEntity.getId();
}
return null;
}
@Cacheable(key = "#pId")
@Override
public RoleEntity check(Serializable pId) {
return super.check(pId);
}
@CachePut(key = "#pId")
@Override
public RoleEntity savePull(Long pId,RoleEntity pEntity) {
return super.savePull(pId,pEntity);
}
@CachePut(key = "#pId")
@Override
public RoleEntity updatePull(Long pId, RoleEntity pEntity) {
return super.updatePull(pId,pEntity);
}
@CacheEvict(key = "#pId")
@Override
public RoleEntity deleteById(Serializable pId) {
return super.deleteById(pId);
}
@Override
protected RoleEntity onDelete(RoleEntity pDeletingEntity){
mRedisUtil.delete("sys_role-byCode::"+pDeletingEntity.getCode());
return pDeletingEntity;
}
}
RelUserRoleDbServiceImpl
@RequiredArgsConstructor
@CacheConfig(cacheNames = "sys_rel_user_role")
@Service
public class RelUserRoleDbServiceImpl extends ZfDbServiceImpl<RelUserRoleMapper, RelUserRoleEntity> implements IRelUserRoleDbService {
@Cacheable(key = "#pUserId")
@Override
public List<RelUserRoleEntity> listByUserId(Long pUserId) {
LambdaQueryWrapper<RelUserRoleEntity> queryWrapper = Wrappers.<RelUserRoleEntity>lambdaQuery()
.eq(RelUserRoleEntity::getUserId,pUserId);
List<RelUserRoleEntity> relUserRoleEntityList = list(queryWrapper);
return relUserRoleEntityList;
}
@CachePut(key = "#pUserId")
@Override
public List<RelUserRoleEntity> edit(Long pUserId, List<RelUserRoleEntity> relRoles) {
LambdaUpdateWrapper<RelUserRoleEntity> queryWrapper = Wrappers.<RelUserRoleEntity>lambdaUpdate()
.eq(RelUserRoleEntity::getUserId,pUserId);
remove(queryWrapper);
saveBatch(relRoles);
return relRoles;
}
@CacheEvict(key = "#pUserId")
@Override
public boolean deleteByUserId(Long pUserId) {
LambdaUpdateWrapper<RelUserRoleEntity> queryWrapper = Wrappers.<RelUserRoleEntity>lambdaUpdate()
.eq(RelUserRoleEntity::getUserId,pUserId);
remove(queryWrapper);
return true;
}
}
UserWithRoleDbServiceImpl
@Service
public class UserWithRoleDbServiceImpl extends ZfDbServiceImpl<UserWithRoleMapper, UserWithRoleEntity> implements IUserWithRoleDbService {
}
3.2.6.2.3 UserRelevantDaoImpl
这个类是对应2.4.2 DAO层中接口的实现
@RequiredArgsConstructor
@Service
public class UserRelevantDaoImpl implements IUserRelevantDao {
private final IUserDbService mUserDbService;
private final IRoleDbService mRoleDbService;
private final IRelUserRoleDbService mRelUserRoleDbService;
@Override
public BaseUserEntity user_generate() {
return new UserEntity();
}
@Override
public BaseUserEntity user_check(Long id) {
return mUserDbService.check(id);
}
@Override
public Long user_findByUserName(String pUserName) {
return mUserDbService.findByUserName(pUserName);
}
protected UserEntity checkUserEntity(BaseUserEntity pBaseUserEntity){
if(! (pBaseUserEntity instanceof UserEntity)){
throw new ServiceException("存储用户数据必须是UserEntity,不能是基类");
}
return (UserEntity)pBaseUserEntity;
}
@Override
public BaseUserEntity user_save(BaseUserEntity pBaseUserEntity) {
return mUserDbService.savePull(pBaseUserEntity.getId(),checkUserEntity(pBaseUserEntity));
}
@Override
public BaseUserEntity user_edit(Long pId, BaseUserEntity pBaseUserEntity) {
return mUserDbService.updatePull(pId,checkUserEntity(pBaseUserEntity));
}
@Override
public BaseUserEntity user_removeById(Long pId) {
return mUserDbService.deleteById(pId);
}
@Override
public List<BaseRoleEntity> role_list() {
return new ArrayList(mRoleDbService.list());
}
@Override
public Long role_findByCode(String pCode) {
return mRoleDbService.findByCode(pCode);
}
@Override
public BaseRoleEntity role_check(Long pId) {
return mRoleDbService.check(pId);
}
protected RoleEntity checkRoleEntity(BaseRoleEntity pRoleEntity){
if(! (pRoleEntity instanceof RoleEntity)){
throw new ServiceException("存储用户数据必须是UserEntity,不能是基类");
}
return (RoleEntity)pRoleEntity;
}
@Override
public BaseRoleEntity role_save(BaseRoleEntity pBaseRoleEntity) {
RoleEntity roleEntity = checkRoleEntity(pBaseRoleEntity);
mRoleDbService.savePull(roleEntity.getId(),roleEntity);
return roleEntity;
}
@Override
public BaseRelUserRoleEntity relUserRole_generate() {
RelUserRoleEntity relUserRoleEntity = new RelUserRoleEntity();
return relUserRoleEntity;
}
@Override
public List<BaseRelUserRoleEntity> relUserRole_listByUserId(Long pUserId) {
return new ArrayList<>(mRelUserRoleDbService.listByUserId(pUserId));
}
@Override
public boolean relUserRole_updateRoles(Long pUserId, List<BaseRelUserRoleEntity> relUserRoleEntityList) {
List<RelUserRoleEntity> roleEntityList = new ArrayList<>();
for(BaseRelUserRoleEntity brure : relUserRoleEntityList){
roleEntityList.add((RelUserRoleEntity)brure);
}
mRelUserRoleDbService.edit(pUserId,roleEntityList);
return true;
}
@Override
public BaseUserEntity user_load(JSONObject pUserCfg) {
UserEntity userEntity = pUserCfg.to(UserEntity.class);
userEntity.createInit();
return userEntity;
}
@Override
public BaseRoleEntity role_load(JSONObject pRoleCfg) {
RoleEntity roleEntity = pRoleCfg.to(RoleEntity.class);
roleEntity.createInit();
return roleEntity;
}
}
3.2.7 handler
package indi.zhifa.recipe.bailan.busy.auth.handler;
@Component
public class MetaObjectHandler extends DefaultMetaObjectHandler {
public MetaObjectHandler(IBaseTokenUtil pTokenUtil){
super(pTokenUtil);
}
@Override
protected void otherInsertFill(MetaObject pMetaObject) {}
@Override
protected void otherUpdateFill(MetaObject pMetaObject) {}
}
3.2.8 service
3.2.8.1 UserService
IUserService
package indi.zhifa.recipe.bailan.busy.auth.service;
public interface IUserService extends IBaseUserService {
UserEntity signIn(SignInDto pSignInDto);
UserEntity edit(Long pId, EditUserDto pEditUserDto);
UserVo info(Long pId);
Page<UserVo> page(Integer pCurrent, Integer pSize, String pRoleCode, String pUserName, String pEmail, String pPhone, String pNickName, Gender pGender, Integer pAgeMin, Integer pAgeMax);
}
LoginServiceImpl
package indi.zhifa.recipe.bailan.busy.auth.service.impl;
@Component
public class UserServiceImpl extends BaseUserServiceImpl implements IUserService {
private final IUserDbService mUserDbService;
private final IUserWithRoleDbService mUserWithRoleDbService;
public UserServiceImpl(IUserRelevantDao pUserRelevantDao,
SecurityConfig pSecurityConfig,
PasswordEncoder pPasswordEncoder,
IBaseTokenUtil pBaseTokenUtil,
IUserDbService pUserDbService,
IUserWithRoleDbService pUserWithRoleDbService) {
super(pUserRelevantDao, pSecurityConfig, pPasswordEncoder, pBaseTokenUtil);
mUserDbService = pUserDbService;
mUserWithRoleDbService = pUserWithRoleDbService;
}
@Transactional(rollbackFor = Exception.class)
@Override
public UserEntity signIn(SignInDto pSignInDto) {
Long id = mUserDbService.findByEmail(pSignInDto.getEmail());
if (null != id) {
throw new ServiceException("已经存在邮箱" + pSignInDto.getEmail());
}
id = mUserDbService.findByPhone(pSignInDto.getPhone());
if (null != id) {
throw new ServiceException("已存在手机" + pSignInDto.getPhone());
}
UserEntity userEntity = DbDtoEntityUtil.createFromDto(pSignInDto, UserEntity.class);
super.createUser(userEntity,null);
return userEntity;
}
@Transactional(rollbackFor = Exception.class)
@Override
public UserEntity edit(Long pId, EditUserDto pEditUserDto) {
UserEntity orgUserEntity = mUserDbService.check(pId);
UserEntity newUserEntity = DbDtoEntityUtil.editByDto(orgUserEntity, pEditUserDto, UserEntity.class);
newUserEntity = mUserDbService.updatePull(pId, newUserEntity);
return newUserEntity;
}
@Override
public UserVo info(Long pId) {
UserEntity userEntity = mUserDbService.check(pId);
UserVo userVo = DtoEntityUtil.trans(userEntity, UserVo.class);
List<String> roles = getUserRoles(pId);
userVo.setRoles(roles);
return userVo;
}
@Override
public Page<UserVo> page(Integer pCurrent, Integer pSize, String pRoleCode, String pUserName, String pEmail, String pPhone, String pNickName, Gender pGender, Integer pAgeMin, Integer pAgeMax) {
Page<UserVo> pageCfg = new Page<UserVo>(pCurrent, pSize);
if(null == pAgeMin){
pAgeMin = -1;
}
if(null == pAgeMax){
pAgeMax = -1;
}
if (!StringUtils.hasText(pRoleCode)) {
LambdaQueryWrapper<UserEntity> queryWrapper = Wrappers.<UserEntity>lambdaQuery()
.eq(StringUtils.hasText(pUserName), UserEntity::getName, pUserName)
.eq(StringUtils.hasText(pEmail), UserEntity::getEmail, pEmail)
.eq(StringUtils.hasText(pPhone), UserEntity::getPhone, pPhone)
.eq(null != pGender, UserEntity::getGender, pGender)
.le(-1 != pAgeMin && -1 == pAgeMax, UserEntity::getBirthday, LocalDate.now().minusYears(pAgeMin))
.ge(-1 != pAgeMax && -1 == pAgeMin, UserEntity::getBirthday, LocalDate.now().minusYears(pAgeMax))
.between(-1 != pAgeMax && -1 != pAgeMin, UserEntity::getBirthday, LocalDate.now().minusYears(pAgeMin), LocalDate.now().minusYears(pAgeMax))
.like(StringUtils.hasText(pNickName), UserEntity::getNickName, pNickName);
Page<UserVo> rtn = mUserDbService.page(pageCfg, queryWrapper, UserVo.class, userEntity -> {
UserVo userVo = DtoEntityUtil.trans(userEntity, UserVo.class);
List<String> roles = getUserRoles(userVo.getId());
userVo.setRoles(roles);
return userVo;
});
return rtn;
} else {
LambdaQueryWrapper<UserWithRoleEntity> queryWrapper = Wrappers.<UserWithRoleEntity>lambdaQuery()
.eq(UserWithRoleEntity::getRoleCode, pRoleCode)
.eq(StringUtils.hasText(pUserName), UserWithRoleEntity::getName, pUserName)
.eq(StringUtils.hasText(pEmail), UserWithRoleEntity::getEmail, pEmail)
.eq(StringUtils.hasText(pPhone), UserWithRoleEntity::getPhone, pPhone)
.eq(null != pGender, UserWithRoleEntity::getGender, pGender)
.le(-1 != pAgeMin && -1 == pAgeMax, UserWithRoleEntity::getBirthday, LocalDate.now().minusYears(pAgeMin))
.ge(-1 != pAgeMax && -1 == pAgeMin, UserWithRoleEntity::getBirthday, LocalDate.now().minusYears(pAgeMax))
.between(-1 != pAgeMax && -1 != pAgeMin, UserWithRoleEntity::getBirthday, LocalDate.now().minusYears(pAgeMin), LocalDate.now().minusYears(pAgeMax))
.like(StringUtils.hasText(pNickName), UserWithRoleEntity::getNickName, pNickName);
Page<UserVo> rtn = mUserWithRoleDbService.page(pageCfg, queryWrapper, UserVo.class, userEntity -> {
UserVo userVo = DtoEntityUtil.trans(userEntity, UserVo.class);
List<String> roles = getUserRoles(userVo.getId());
userVo.setRoles(roles);
return userVo;
});
return rtn;
}
}
}
3.2.8.2 LoginService
ILoginService
package indi.zhifa.recipe.bailan.busy.auth.service;
public interface ILoginService extends IBaseLoginService {
}
LoginServiceImpl
package indi.zhifa.recipe.bailan.busy.auth.service.impl;
@Component
public class LoginServiceImpl extends BaseLoginServiceImpl implements ILoginService {
private final IUserDbService mUserDbService;
public LoginServiceImpl(IUserRelevantDao pUserRelevantDao,
SecurityConfig pSecurityConfig,
PasswordEncoder pPasswordEncoder,
IBaseTokenUtil pBaseTokenUtil,
IUserDbService pUserDbService) {
super(pUserRelevantDao, pSecurityConfig, pPasswordEncoder, pBaseTokenUtil);
mUserDbService = pUserDbService;
}
protected BaseUserEntity checkUserEntity(Long pId) {
UserEntity userEntity = mUserDbService.check(pId);
return userEntity;
}
@Override
protected BaseTokenObject fillOtherInfo(BaseTokenObject pBaseTokenObject, BaseUserEntity pBaseUserEntity) {
TokenObject tokenObject = (TokenObject)pBaseTokenObject;
UserEntity userEntity = (UserEntity)pBaseUserEntity;
tokenObject.setNickName(userEntity.getNickName());
return tokenObject;
}
}
3.2.9 tokenUtil实现
TokenUtilImpl
package indi.zhifa.recipe.bailan.busy.auth.util;
@Component
public class TokenUtilImpl extends BaseTokenUtilImpl {
public TokenUtilImpl(SecurityConfig pSecurityConfig){
super(pSecurityConfig);
}
@Override
public BaseTokenObject createTokenObject() {
return new TokenObject();
}
@Override
public BaseTokenObject getTokenObject() {
return super.getTokenObject(TokenObject.class);
}
@Override
public BaseTokenObject parseToken(String pToken) {
TokenObject tokenObject = parseToken(pToken,TokenObject.class);
return tokenObject;
}
}