芝法酱躺平攻略(8)——SpringBoot下的登录与注册的JWT实现

3 篇文章 0 订阅
1 篇文章 0 订阅

一、前言

做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;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值