芝法酱躺平攻略(9)——SpringBoot+mybatisplus的鉴权设计-1

本文详细介绍了在SpringBoot和Mybatis-Plus环境下,如何设计和实现权限系统,包括角色鉴权、数据权限、注解设计以及数据权限拦截器的实现。此外,还讨论了如何利用AOP和自定义注解实现接口级别的权限控制,以及数据库视图在权限查询中的应用。
摘要由CSDN通过智能技术生成

一、权限的设计

在芝法酱躺平攻略(9)中,介绍了在SpringBoot下如何实现登陆注册,并对没有登陆的用户进行拦截。在这节,我们将深入的讨论在SpringBoot + mybatis-plus下的鉴权设计。

1.1 鉴权的种类和范围

鉴权包括角色鉴权和数据与鉴权。
角色鉴权指的是,用户拥有特定角色,则可以访问特定接口。比如,只有
数据权限指的是,同样的查询接口,不同的用户可查到的数据不同。在操作数据时,如果特定数据不在用户的管辖范围,则拒绝操作。
我们举一个实际的例子,某设备管理系统,有一个树级的部门。不同的设备放在不同的部门节点,不同的管理员分配到不同的部门节点中。管理员只能查看部门下的设备。在修改设备信息的接口:DeviceApi::edit(Long pId, DeviceDto pDeviceDto);如果该Id的设备不在管理员的管辖范围,则拒绝修改。

1.2 使用AOP把鉴权与业务代码分离

如果仅仅想实现鉴权,这并不困难。在个接口的实现中,加上鉴权相关的代码即可实现。但这样就带来了一个问题,在每个接口都需要编写相关的鉴权代码。还需要测试同学挨个测试,看各接口的鉴权是否有疏漏。当鉴权逻辑发生了修改,还需要在各个接口中一一修改。如果接口较多,还要保证每个接口的实现都是一致的。而且每个地方都写一通,也十分费劲。
好在,Spring有一个很妙的AOP系统,可以在接口上写一个注解,通过AOP拦截各接口,做统一的鉴权处理。
但也要注意,有些特殊的逻辑还是要写在具体的service中,不要为了使用AOP而去使用AOP。

1.3 设备管理系统需求分析

拿设备管理系统举例,系统中有这些元素,用户,组织-用户的节点树,公司,设备,设备群组-设备的权限节点树。
用户有这几类:
超级管理员,拥有系统的所有权限,可查阅一切数据,修改删除一切数据。
用户账户管理员,处于组织的某节点,可以修改该组织和该组织子孙节点的用户信息。
系统管理员,可以设置系统的参数,创建可应用于设备群组的规则。
公司用户,可录入设备信息,修改查看公司下的所有设备。
设备群组管理员,可以管理设备群组以及该群组节点和其子孙节点,可以把设备添加到自己所管理的群组,也可以把相应设备移出自己所管理的群组。

1.4 领域部门设计

如果,我们把系统中的实体分成2类,1类是用户,一类是资源。由上面的需求我们可得出,用户可能属于多个权限节点树,资源也可能属于多个部门。这看上去就有点麻烦了。但如果我们引入领域这个概念,一定程度上就简化了这种逻辑。
我们认为,组织、设备群组、公司,这是分属于3个领域。但对于每个用户来说,在每个特定领域,只可能属于1个或不属于任何部门,这种部门的设定需求支持动态管理。而设备这种资源实体,虽说也可能在多个领域内属于某个部门,但至于属于哪个领域,这是绑定业务逻辑定死的。所以,至于属于哪个鉴权节点,直接在实体中记录即可,不需要引入关联表。
对于组织、设备群组、公司,其权限部分都属于权限节点树表,而额外的信息也需要各自单独建一张表来管理。
为此,我展示一下我设计的数据库表:
注意,在一个领域下,部门的code是唯一的。因为cascade不希望太长,所以要用一个code做主键。
在这里插入图片描述

在这里插入图片描述

1.5 资源相关表的设计

对于资源的表设计, 我这里展示设备表的设计
在这里插入图片描述
我们可以看到,设备的权限节点在2个领域内有体现,一个是群组层面,一个是公司层面。
在群组层面,我们查询时希望根据群组管理员的所在群组节点,查其节点和其子孙节点的数据。体现在sql上就是where group_auth_cascade like ${用户所在群组的级联码}%
在公司层面,我们希望查询用户所在公司的设备。体现在sql上就是where company_auth_id = ${用户所在公司的Id}

1.6 用户相关表的设计

除了芝法酱躺平攻略(8)中的用户字段,我们还需要再引入一个权限节点的关联表
在这里插入图片描述

1.7 注解设计

1.7.1 TableAuth

/**
 * 放在mapper上,标注对应实体的鉴权相应列名
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TableAuth {

    /**
     * 适用于谁创建就谁管理的鉴权模式,如博客的编辑
     *
     * @return
     */
    String relUserIdColumn() default "create_by";

    /**
     * 领域数组
     *
     * @return
     */
    String[] domains() default {"default"};

    /**
     * 对应domains的权限Id列名
     *
     * @return
     */
    String[] idColumns() default {"auth_id"};
    String[] cascadeColumns() default {"auth_cascade"};
}

1.7.2 RequireRole

/**
 * 通常放在controller上,标注该接口所需的角色
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireRole {

    /**
     * 可通行的角色数组,或关系
     * @return
     */
    String[] roles() default {};
}

1.7.3 DataPermission

/**
 * 数据域权限,通常放到controller上
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataPermission {

    /**
     * 鉴权类型
     * @return
     */
    EDataPermissionType permissionType() default EDataPermissionType.DEP_CASCADE;

    /**
     * 鉴权领域
     * @return
     */
    String domain();

    /**
     * 豁免的角色
     * @return
     */
    String[] exemptionRoles() default {"spAdm"};
}

1.7.4 EDataPermissionType

package indi.zhifa.recipe.bailan.framework.auth.entity.enums;

@RequiredArgsConstructor
@EnumDesc(desc = "数据权限类型")
public enum EDataPermissionType {

    ID_EQ(1,"自有权限","在SQL中拼XX=#userId"),
    AUTH_ID(2,"鉴权节点ID权限","在SQL中拼authId = #userAuthId"),
    AUTH_CASCADE(3,"级联权限","在SQL中拼depCascade like #userAuthCascade%"),
    AUTH_CASCADE_CHILDREN(4,"级联子项","在SQL中拼authCascade like #userAuthCascade-%")
    ;

    @EnumValue
    @Getter
    final int code;
    @Getter
    final String name;
    @Getter
    final String description;
}

1.8 用户权限+角色的视图

为了方便用户的查询,必须做用户表,用户权限表,用户权限关联表,用户角色关联表的join查询。
而在mybatis-plus中,这种联表查询+分页的需求做着十分麻烦,故小编推荐在数据库里建一个视图,使用视图做查询。
很多人很反对数据库里放视图,但我没当问道原因时,也没能听到具体且有说服力的答案。如果知道的小伙伴欢迎私信我。

1.8.1 视图的sql

CREATE 
VIEW `view_user_with_role_and_auth` 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_rel_user_auth`.`domain_id` AS `domain_id`,
        `sys_rel_user_auth`.`domain_code` AS `domain_code`,
        `sys_rel_user_auth`.`auth_id` AS `auth_id`,
        `sys_rel_user_auth`.`auth_code` AS `auth_code`,
        `sys_rel_user_auth`.`auth_cascade` AS `auth_cascade`,
        `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`.`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`)))
        JOIN `sys_rel_user_auth` ON ((`sys_user`.`id` = `sys_rel_user_auth`.`user_id`)))

1.8.2 视图的实体

BaseUserWithRoleAndAuthViewEntity

package indi.zhifa.recipe.bailan.framework.auth.entity.po;

@Data
public class BaseUserWithRoleAndAuthViewEntity {
    @Schema(title = "主键")
    @TableId(type = IdType.ASSIGN_ID)
    protected Long id;

    @Schema(title = "用户名")
    protected String name;

    @Schema(title = "角色码")
    protected String roleCode;

    @Schema(title = "角色Id")
    protected String roleId;

    @Schema(title = "部门Id")
    protected Long authId;

    @Schema(title = "部门Code")
    protected String authCode;

    @Schema(title = "部门级联")
    protected String authCascade;

}

UserWithRoleAndAuthViewEntity

@TableName("view_user_with_role_and_auth")
@Schema(title = "用户和角色权限节点的join表视图", description = "用户和角色权限节点的join表视图")
@Data
public class UserWithRoleAndAuthViewEntity extends BaseUserWithRoleAndAuthViewEntity {

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

二、数据权限的实现原理

2.1 RequireRole注解的实现

这个注解的实现十分简单,写一个AOP就行,这里直接上代码了

package indi.zhifa.recipe.bailan.framework.auth.filter;

@Order(value = 100)
@Slf4j
@Aspect
@RequiredArgsConstructor
@Component
public class RequireRoleAop {

    protected final IBaseTokenUtil mBaseTokenUtil;

    @Before("@annotation(permission)")
    public void before(JoinPoint point, RequireRole permission){
        BaseTokenObject baseTokenObject = mBaseTokenUtil.getTokenObject();
        // 获取用户的角色
        List<String> roles = baseTokenObject.getRoles();
        Set<String> roleSet = roles.stream().collect(Collectors.toSet());
        if(roleSet.contains(AuthConst.spAdmCode)){//超管角色直接跳过,不需要在每个注解中配置了
            return;
        }
        //便利所有的可通行的角色,如果用户有这个角色,则return
        String[] permitRoles = permission.roles();
        for(String permitRole : permitRoles){
            if(roleSet.contains(permitRole)){
                return;
            }
        }
        // 所有所需的角色都没有,那就报错
        throw new ServiceException("需要角色"+String.join(",",permitRoles));
    }
}

2.2 DataPermissionHandler

mybatis-plus的DataPermissionHandler提供了数据权限的拦截器,我们只要重载他的Expression getSqlSegment(Expression where, String mappedStatementId),就可以修改sql,拼接数据鉴权的相关sql。
但拼接sql时,我们需要知道哪些字段是对应的鉴权字段,采用什么策略鉴权等。这些信息可以配置到相应1.5.1的TableAuth注解和1.5.3的DataPermission注解中。同时,我们还可以把自己的实现类加上AOP的注解@Aspect,使我们判断特定请求是否需要做数据域鉴权,并放入用户相关的信息等。

2.3 TableAuth注解从mapper中获取鉴权的列

TableAuth注解配置了哪些字段是鉴权相关的列。可以通过mappedStatementId参数获得配置到mapper上的类的全路径名,从而通过反射取得配置在mapper类上的TableAuth。我们可以写个简单的内存缓存把处理好的结果存起来,避免重复反射。

2.3.1 TableAuthMapItem

@Data
package indi.zhifa.recipe.bailan.framework.auth.entity.dto.auth;

public class TableAuthMapItem {
    TableAuth tableAuth;
    boolean exist;
    boolean error;
    Map<String, TableAuthColumn> tableAuthColumnsMap;
    String relUserColumnName;
}

2.3.2 TableAuthColumn

package indi.zhifa.recipe.bailan.framework.auth.entity.dto.auth;

@Data
public class TableAuthColumn {
    String idColumn;
    String cascadeColumn;
}

2.3.3 MapperAuthMapMemo

package indi.zhifa.recipe.bailan.framework.auth.memo;

@Slf4j
@Component
public class MapperAuthMapMemo {
    private final Map<String, TableAuthMapItem> mMapperAuthMap;
    public MapperAuthMapMemo(){
        mMapperAuthMap = new HashMap<>();
    }
    public void put(String pClsName, TableAuthMapItem pTableAuthMapItem){
        mMapperAuthMap.put(pClsName,pTableAuthMapItem);
    }
    public TableAuthMapItem loadClass(String pMappedStatementId){
        int lastDotIdx = pMappedStatementId.lastIndexOf('.');
        String clsName = pMappedStatementId.substring(0,lastDotIdx);
        TableAuthMapItem tableAuthMapItem = mMapperAuthMap.get(clsName);
        if(null == tableAuthMapItem){
            tableAuthMapItem = new TableAuthMapItem();
            mMapperAuthMap.put(clsName,tableAuthMapItem);
            Class<Object> mapperCls = ClassUtil.loadClass(clsName);
            TableAuth annotation = mapperCls.getAnnotation(TableAuth.class);
            if(null != annotation){
                tableAuthMapItem.setTableAuth(annotation);
                tableAuthMapItem.setExist(true);
                boolean hasError = !testTableAuth(clsName,annotation);
                tableAuthMapItem.setError(hasError);
                if(!hasError){
                    Map<String, TableAuthColumn> tableAuthColumnMap = getTableAuthColumnMap(clsName,annotation);
                    tableAuthMapItem.setTableAuthColumnsMap(tableAuthColumnMap);
                }
                tableAuthMapItem.setRelUserColumnName(annotation.relUserIdColumn());
            }else{
                tableAuthMapItem.setTableAuth(null);
                tableAuthMapItem.setExist(false);
            }
        }
        return tableAuthMapItem;
    }

    private boolean testTableAuth(String pClsName, TableAuth pTableAuth){
        String[] domains = pTableAuth.domains();
        String[] idColumns = pTableAuth.idColumns();
        String[] cascadeColumns = pTableAuth.cascadeColumns();
        if(null != domains && domains.length > 0){
            if(null == idColumns){
                log.error(pClsName+"没有配置idColumns");
                return false;
            }
            if(null == cascadeColumns){
                log.error(pClsName+"没有配置cascadeColumns");
                return false;
            }
            if(domains.length!= idColumns.length){
                log.error(pClsName+"idColumns长度与domains不同");
                return false;
            }
            if(domains.length!= cascadeColumns.length){
                log.error(pClsName+"cascadeColumns长度与domains不同");
                return false;
            }
        }
        return true;
    }
    private Map<String, TableAuthColumn> getTableAuthColumnMap(String pClsName, TableAuth pTableAuth){
        String[] domains = pTableAuth.domains();
        String[] idColumns = pTableAuth.idColumns();
        String[] cascadeColumns = pTableAuth.cascadeColumns();
        Map<String, TableAuthColumn> rtn = new HashMap<>();
        for(int i=0;i<domains.length;i++){
            TableAuthColumn tableAuthColumn = new TableAuthColumn();
            tableAuthColumn.setIdColumn(idColumns[i]);
            tableAuthColumn.setCascadeColumn(cascadeColumns[i]);
            String domain = domains[i];
            rtn.put(domain, tableAuthColumn);
        }
        return rtn;
    }
}

2.3.4 DataPermissionHandler的代码实现

package indi.zhifa.recipe.bailan.framework.auth.filter;

@Order(value = 101)
@Aspect
@Slf4j
@RequiredArgsConstructor
@Component
public class DataPermissionAop implements DataPermissionHandler {

    private final IBaseTokenUtil mTokenUtil;
    private final MapperAuthMapMemo mMapperAuthMapMemo;
    private ThreadLocal<DataPermissionData> dataPermissionDataThreadLocal = new ThreadLocal<>();

    @Before("@annotation(permission)")
    public void doBefore(JoinPoint point, DataPermission permission) throws Throwable {
        if (!mTokenUtil.hasTokenObject()) {
            return;
        }
        BaseTokenObject baseTokenObject = mTokenUtil.getTokenObject();
        Set<String> roleCodes = baseTokenObject.getRoles().stream().collect(Collectors.toSet());
        String[] exemptionRoles = permission.exemptionRoles();
        DataPermissionData dataPermissionData = new DataPermissionData();
        dataPermissionData.setUserId(baseTokenObject.getId());
        dataPermissionData.setDepInfo(baseTokenObject.getAuthNodeInfo());
        dataPermissionData.setExemption(false);
        dataPermissionData.setDataPermission(permission);
        for(String exemptionRole : exemptionRoles){
            if(roleCodes.contains(exemptionRole)){
                dataPermissionData.setExemption(true);
            }
        }
        dataPermissionDataThreadLocal.set(dataPermissionData);
    }

    @After("@annotation(permission)")
    public void doAfter(JoinPoint point,DataPermission permission) {
        dataPermissionDataThreadLocal.remove();
    }

    @Override
    public Expression getSqlSegment(Expression where, String mappedStatementId) {
        DataPermissionData dataPermissionData = dataPermissionDataThreadLocal.get();
        if (null == dataPermissionData) {
            return where;
        }
        if(dataPermissionData.isExemption()){
            return where;
        }
        TableAuthMapItem tableAuthMapItem = mMapperAuthMapMemo.loadClass(mappedStatementId);
        if(!tableAuthMapItem.isExist()){
            return where;
        }

        DataPermission dataPermission = dataPermissionData.getDataPermission();
        EDataPermissionType dataPermissionType = dataPermission.permissionType();
        String domain = dataPermission.domain();
        if(EDataPermissionType.ID_EQ == dataPermissionType){
            String relUserIdColumnName = tableAuthMapItem.getRelUserColumnName();
            if(!StringUtils.hasText(relUserIdColumnName)){
                return where;
            }
            Column relUserIdColumn = new Column(relUserIdColumnName);
            LongValue userId = new LongValue(dataPermissionData.getUserId());
            EqualsTo equalsTo = new EqualsTo(relUserIdColumn, userId);
            return new AndExpression(where,equalsTo);
        }else if(EDataPermissionType.AUTH_ID == dataPermissionType){
            if(tableAuthMapItem.isError()){
                return where;
            }
            Map<String, TableAuthColumn> tableAuthColumnsMap = tableAuthMapItem.getTableAuthColumnsMap();
            Map<String, TokenAuthNodeDto> tokenDepartmentDtoMap = dataPermissionData.getDepInfo();
            TableAuthColumn tableAuthColumn = tableAuthColumnsMap.get(domain);
            if(null == tableAuthColumn){
                return where;
            }
            TokenAuthNodeDto tokenAuthNodeDto = tokenDepartmentDtoMap.get(domain);
            if(null == tokenAuthNodeDto){
                return where;
            }
            String depIdColumnName = tableAuthColumn.getIdColumn();
            if(!StringUtils.hasText(depIdColumnName)){
                return where;
            }
            Column depIdColumn = new Column(depIdColumnName);
            LongValue depId = new LongValue(tokenAuthNodeDto.getAuthId());
            EqualsTo equalsTo = new EqualsTo(depIdColumn, depId);
            return new AndExpression(where,equalsTo);
        }else if(EDataPermissionType.AUTH_CASCADE == dataPermissionType){
            if(tableAuthMapItem.isError()){
                return where;
            }
            Map<String, TableAuthColumn> tableAuthColumnsMap = tableAuthMapItem.getTableAuthColumnsMap();
            Map<String, TokenAuthNodeDto> tokenDepartmentDtoMap = dataPermissionData.getDepInfo();
            TableAuthColumn tableAuthColumn = tableAuthColumnsMap.get(domain);
            if(null == tableAuthColumn){
                return where;
            }
            TokenAuthNodeDto tokenAuthNodeDto = tokenDepartmentDtoMap.get(domain);
            if(null == tokenAuthNodeDto){
                return where;
            }
            String depCascadeColumnName = tableAuthColumn.getCascadeColumn();
            if(!StringUtils.hasText(depCascadeColumnName)){
                return where;
            }
            Column depCascadeColumn = new Column(depCascadeColumnName);
            StringValue depCascade = new StringValue(tokenAuthNodeDto.getAuthCascade()+"%");
            LikeExpression likeExpression = new LikeExpression();
            likeExpression.setLeftExpression(depCascadeColumn);
            likeExpression.setRightExpression(depCascade);
            return new AndExpression(where,likeExpression);
        }else if(EDataPermissionType.AUTH_CASCADE_CHILDREN == dataPermissionType){
            if(tableAuthMapItem.isError()){
                return where;
            }
            Map<String, TableAuthColumn> tableAuthColumnsMap = tableAuthMapItem.getTableAuthColumnsMap();
            Map<String, TokenAuthNodeDto> tokenDepartmentDtoMap = dataPermissionData.getDepInfo();
            TableAuthColumn tableAuthColumn = tableAuthColumnsMap.get(domain);
            if(null == tableAuthColumn){
                return where;
            }
            TokenAuthNodeDto tokenAuthNodeDto = tokenDepartmentDtoMap.get(domain);
            if(null == tokenAuthNodeDto){
                return where;
            }
            String depCascadeColumnName = tableAuthColumn.getCascadeColumn();
            if(!StringUtils.hasText(depCascadeColumnName)){
                return where;
            }
            Column depCascadeColumn = new Column(depCascadeColumnName);
            StringValue depCascade = new StringValue(tokenAuthNodeDto.getAuthCascade()+"-%");
            LikeExpression likeExpression = new LikeExpression();
            likeExpression.setLeftExpression(depCascadeColumn);
            likeExpression.setRightExpression(depCascade);
            return new AndExpression(where,likeExpression);
        }

        return where;
    }

}

2.3.5 配置类

配置类这点多说两句,在我们的framework-mysql中,已经写过了mybatisplus的配置类,那么,我们如何再添加一个配置呢?我当时思考了好长时间,终于想到一个办法,把mybatisplus的配置类注入进来,在@PostConstruct动态添加InnerInterceptor
framework-mysql:BaseMybatisPlusConfig

package indi.zhifa.recipe.bailan.framework.mysql.config;

@Slf4j
@AllArgsConstructor
@Configuration
public class BaseMybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 分页
        PaginationInnerInterceptor mysqlInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        interceptor.addInnerInterceptor(mysqlInnerInterceptor);

        // 乐观锁
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        log.info("======================BaseMybatisPlusConfig==========================");

        return interceptor;
    }
}

AuthMybatisPlusConfig

package indi.zhifa.recipe.bailan.framework.auth.config;

@Slf4j
@AllArgsConstructor
@Configuration
public class AuthMybatisPlusConfig {

    private final DataPermissionAop mDataPermissionAop;
    private final MybatisPlusInterceptor mMybatisPlusInterceptor;



    @PostConstruct
    public void mybatisPlusInterceptor() {

        //  数据权限
        DataPermissionInterceptor dataPermissionInterceptor = new DataPermissionInterceptor();
        dataPermissionInterceptor.setDataPermissionHandler(mDataPermissionAop);
        mMybatisPlusInterceptor.addInnerInterceptor(dataPermissionInterceptor);
        log.info("======================AuthMybatisPlusConfig==========================");
    }
}

三、实体类和dao层展示

由于本篇的逻辑代码量巨大,故打算只展示核心逻辑。增删改查的代码尽量省略。一些实体小伙伴们可以根据数据库设计图自行创建。
在基类的实体中,仅仅存放诸如Id,code,关联Id,冗余的关联Code,cascade等字段。把诸如名字,描述,还有其他个性化的东西,放在子类中。本篇只截图所涉及的类,并以鉴权节点为例做代码示例

3.1 基类和子类截图

3.1.1 基类

在这里插入图片描述

3.1.2 子类

在这里插入图片描述

3.3 AuthNodeEntity示例

3.3.1 BaseAuthNodeEntity

package indi.zhifa.recipe.bailan.framework.auth.entity.po;

@Data
public class BaseAuthNodeEntity extends SysBaseEntity {

    protected BaseAuthNodeEntity(){

    }

    @Schema(title = "领域id")
    protected Long domainId;

    @Schema(title = "领域code")
    protected String domainCode;

    @Schema(title = "父节点id")
    protected Long parentId;

    @Schema(title = "父节点code")
    protected String parentCode;

    @Schema(title = "部门码")
    protected String code;

    @TableField(value = "`cascade`")
    @Schema(title = "部门级联码")
    protected String cascade;

}

3.3.2 AuthNodeEntity

package indi.zhifa.recipe.bailan.busy.auth.entity.po;

@Data
@TableName("sys_auth_node")
@Schema(title = "鉴权节点对象", description = "部门表")
public class AuthNodeEntity extends BaseAuthNodeEntity {
    @Schema(title = "节点名")
    protected String name;

    @Schema(title = "节点描述")
    protected String description;
}

3.3 基类中用到的dao

为了不把业务定死,我们不希望在基类定死实体。但由于mybatis-plus对实体的继承支持的不是很好,故在基类中,我们自己定义一些dao的接口,在子类中使用mybatis-plus的方法实现它。这样我们就可以在基类的BaseService中注入我们的dao接口。
由于篇幅限制,本篇只展示接口,小伙伴们可以根据接口名字自己去实现。

3.3.1 IBaseUserRelevantDao

public interface IBaseUserRelevantDao {
    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);

}

3.3.2 IBaseAuthNodeRelevantDao

public interface IBaseAuthNodeRelevantDao {

    BaseDomainEntity domain_generate();
    BaseDomainEntity domain_load(JSONObject pCfg);
    BaseDomainEntity domain_check(Long pId);
    Long domain_findByCode(String pCode);
    default BaseDomainEntity domain_check(String pCode){
        Long id = domain_findByCode(pCode);
        if(null == id){
            throw new ServiceException("没有找到名为"+pCode+"的领域");
        }
        return domain_check(id);
    }
    default boolean domain_existByCode(String pCode){
        Long id = domain_findByCode(pCode);
        return null != id;
    }

    List<BaseDomainEntity> domain_list();


    BaseDomainEntity authNode_save(BaseDomainEntity pBaseDomainEntity);

    BaseAuthNodeEntity authNode_generate();
    BaseAuthNodeEntity authNode_load(JSONObject pCfg);

    Long authNode_findByDomainAndCode(Long pDomainId, String pCode);
    BaseAuthNodeEntity authNode_check(Long pId);

    default boolean authNode_exist(Long pDomainId, String pCode){
        Long id = authNode_findByDomainAndCode(pDomainId,pCode);
        return null != id;
    }

    default BaseAuthNodeEntity authNode_check(Long pDomainId, String pCode){
        Long id = authNode_findByDomainAndCode(pDomainId,pCode);
        if(null == id){
            throw new ServiceException(pDomainId + "下没有找到名为"+pCode+"的部门,或者您没有权限访问它");
        }
        return authNode_check(id);
    }

    BaseAuthNodeEntity authNode_save(BaseAuthNodeEntity pBaseAuthNodeEntity);
    BaseAuthNodeEntity authNode_update(Long pId, BaseAuthNodeEntity pBaseAuthNodeEntity);

    List<BaseAuthNodeEntity> authNode_listByDomainId(Long pDomainId);
    List<BaseAuthNodeEntity> authNode_listByParentId(Long pParentId);
    List<BaseAuthNodeEntity> authNode_listDescendants(String pCascade);
    BaseAuthNodeEntity authNode_delete(Long pId);


    BaseRelUserAuthEntity relUserAuth_generate();

    List<BaseRelUserAuthEntity> relUserAuth_listByUserId(Long pId);
    List<BaseRelUserAuthEntity> relUserAuth_saveBatch(Long pUserId, List<BaseRelUserAuthEntity> pBaseRelUserAuthEntityList);
    void relUserAuth_deleteByUserId(Long pUserId);

}

四、初始化与测试数据

4.1 初始化数据

鉴权系统框架,能提供默认的用户、鉴权节点、角色等信息,这样方便服务的部署。当然,市面上主流做法是使用flyway执行初始的sql。小编认为这种做法很不好维护,不是很推荐。
我更倾向于写一个相关的配置,起服时读取相关配置,自动检测,如果没有创建,则自动创建。

4.1.1 初始化配置

对于初始的数据,我们用json文件存储,而不再像之前存放在yml里,此处先给出文件截图
在这里插入图片描述
auth_ini.json是初始账号,权限节点,角色,用户默认角色和部门等信息的配置,device是初始设备的配置。
由于想要更全面的测试,用手编写json是件痛苦的事,故我们考虑在Excel中编辑数据,用工具导出。

4.1.2 工具下载

小编之前做游戏策划时,自己写过一个Excel导表工具,可以把Excel导出成结构化的lua或json文件。
github地址:https://github.com/hataksumo/ExcelToLua
软件目录截图:
在这里插入图片描述

xml配置:

<?xml version="1.0" encoding="utf-16"?>
<root>
  <package name="CFG"/>
  <app isRelease="true"/>
  <path cli=".\opt\cli\" serv=".\opt\serv\" export=".\opt\export\" excelPath=".\Excel" indexPath=".\Excel\INDEX\INDEX.xlsx" lua_cfg=".\Lua\"/>
</root>

软件运行界面
在这里插入图片描述

4.1.3 Excel和导出数据

请在下一篇查看

4.2 初始化入口

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 IAuthInitService mAuthInitService;
    private final IDeviceService mDeviceService;

    @Override
    public void run(String... args) throws Exception {
        DtoEntityUtil.init();
        mEnumMemoService.initEnum(mEnumMemoConfig.getEnumPackages());
        mAuthInitService.init();
        mDeviceService.init();
    }
}

AuthInitServiceImpl

package indi.zhifa.recipe.bailan.framework.auth.service.impl;

@Component
@Slf4j
@RequiredArgsConstructor
public class AuthInitServiceImpl implements IAuthInitService {

    protected final SecurityConfig mSecurityConfig;
    protected final IBaseUserService mUserService;
    protected final IBaseAuthNodeService mDepartmentService;


    @Override
    public void init() {
        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 defaultUser = authInitConfig.getJSONArray("defaultUser");
        JSONArray domainConfigs = authInitConfig.getJSONArray("defaultDomains");
        Map<String, BaseRoleEntity> iniRoles = mUserService.initRole(defaultRoles);
        Map<String, BaseDomainVo> iniDomain = mDepartmentService.init(domainConfigs);
        mUserService.initUser(defaultUser);
    }
}

4.3 初始化角色

@Component
@Slf4j
@RequiredArgsConstructor
public class AuthInitServiceImpl implements IAuthInitService {

    protected final SecurityConfig mSecurityConfig;
    protected final IBaseUserService mUserService;
    protected final IBaseAuthNodeService mDepartmentService;


    @Override
    public void init() {
        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 defaultUser = authInitConfig.getJSONArray("defaultUser");
        JSONArray domainConfigs = authInitConfig.getJSONArray("defaultDomains");
        Map<String, BaseRoleEntity> iniRoles = mUserService.initRole(defaultRoles);
        Map<String, BaseDomainVo> iniDomain = mDepartmentService.init(domainConfigs);
        mUserService.initUser(defaultUser);
    }
}

4.4 初始化部门

4.4.1 基类实现

package indi.zhifa.recipe.bailan.framework.auth.service.impl;

@RequiredArgsConstructor
public abstract class BaseAuthNodeServiceImpl implements IBaseAuthNodeService {

    private final IBaseAuthNodeRelevantDao mDepartmentRelevantDao;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, BaseDomainVo> init(JSONArray pDomainConfigs){
        Map<String, BaseDomainVo> rtn = new HashMap<>();

        // 处理领域和部门
        for (int i = 0; i < pDomainConfigs.size(); i++) {
            JSONObject jsonObject = pDomainConfigs.getJSONObject(i);
            String domainCode = jsonObject.getString("code");
            BaseDomainEntity baseDomainEntity;
            Long domainId = mDepartmentRelevantDao.domain_findByCode(domainCode);
            if (null != domainId) {
                baseDomainEntity = mDepartmentRelevantDao.domain_check(domainId);
                List<BaseAuthNodeEntity> descendant = mDepartmentRelevantDao.authNode_listByDomainId(domainId);
                BaseDomainVo baseDomainVo = DtoEntityUtil.trans(baseDomainEntity,BaseDomainVo.class);
                Map<String, BaseAuthNodeEntity> departmentEntityMap = baseDomainVo.getDepartmentEntityMap();
                for(BaseAuthNodeEntity baseAuthNodeEntity : descendant){
                    departmentEntityMap.put(baseAuthNodeEntity.getCode(), baseAuthNodeEntity);
                }
                rtn.put(domainCode,baseDomainVo);
                continue;
            }

            baseDomainEntity = mDepartmentRelevantDao.domain_load(jsonObject);
            baseDomainEntity.createInit();
            baseDomainEntity.setCode(domainCode);
            mDepartmentRelevantDao.authNode_save(baseDomainEntity);
            BaseDomainVo baseDomainVo = DtoEntityUtil.trans(baseDomainEntity,BaseDomainVo.class);
            Map<String, BaseAuthNodeEntity> departmentEntityMap = new HashMap<>();
            baseDomainVo.setDepartmentEntityMap(departmentEntityMap);
            rtn.put(baseDomainVo.getCode(),baseDomainVo);

            // 处理部门
            AuthNodeCfg authNodeCfg = new AuthNodeCfg();//创建命令
            authNodeCfg.setCode("rt");
            authNodeCfg.setCascadeCode("rt");
            authNodeCfg.setParentCode("rt");
            authNodeCfg.setParentId(-1L);
            authNodeCfg.setCfg(jsonObject);
            Queue<AuthNodeCfg> que = new ArrayDeque<>();
            que.add(authNodeCfg);

            List<BaseAuthNodeEntity> savingBaseAuthNodeEntityList = new ArrayList<>();
            List<AuthNodeCfg> authNodeCfgList = new ArrayList<>();
            while (!que.isEmpty()) {
                AuthNodeCfg curCfg = que.poll();
                BaseAuthNodeEntity baseAuthNodeEntity = mDepartmentRelevantDao.authNode_load(curCfg.getCfg());
                baseAuthNodeEntity.setDomainId(baseDomainEntity.getId());
                baseAuthNodeEntity.setDomainCode(domainCode);
                baseAuthNodeEntity.setParentId(curCfg.getParentId());
                baseAuthNodeEntity.setParentCode(curCfg.getParentCode());
                baseAuthNodeEntity.setCode(curCfg.getCode());
                baseAuthNodeEntity.setCascade(curCfg.getCascadeCode());
                savingBaseAuthNodeEntityList.add(baseAuthNodeEntity);
                authNodeCfgList.add(curCfg);
                JSONObject curJsonCfg = curCfg.getCfg();
                JSONArray childrenCfg = curJsonCfg.getJSONArray("children");
                if (!CollectionUtils.isEmpty(childrenCfg)) {
                    for (int j = 0; j < childrenCfg.size(); j++) {
                        JSONObject childJsonCfg = childrenCfg.getJSONObject(j);
                        AuthNodeCfg childCfg = new AuthNodeCfg();
                        childCfg.setCfg(childJsonCfg);
                        childCfg.setCode(childJsonCfg.getString("code"));
                        childCfg.setParentId(baseAuthNodeEntity.getId());
                        childCfg.setParentCode(baseAuthNodeEntity.getCode());
                        childCfg.setCascadeCode(curCfg.getCascadeCode() + "-" + childJsonCfg.getString("code"));
                        que.offer(childCfg);
                    }
                }
            }
            for (BaseAuthNodeEntity baseAuthNodeEntity : savingBaseAuthNodeEntityList) {
                departmentEntityMap.put(baseAuthNodeEntity.getCode(), baseAuthNodeEntity);
                if(!mDepartmentRelevantDao.authNode_exist(baseDomainEntity.getId(),baseAuthNodeEntity.getCode())){
                    mDepartmentRelevantDao.authNode_save(baseAuthNodeEntity);
                }
            }
            onInit(domainCode,authNodeCfgList,savingBaseAuthNodeEntityList);
        }
        return rtn;
    }

    protected abstract void onInit(String pDomainCode, List<AuthNodeCfg> pAuthNodeCfgList,List<BaseAuthNodeEntity> pSavingBaseAuthNodeEntityList);
    /*还有其他业务逻辑,不在本小节展示*/
}

4.4.2 子类实现

AuthNodeServiceImpl

package indi.zhifa.recipe.bailan.busy.auth.service.impl;

@Slf4j
@Component
public class AuthNodeServiceImpl extends BaseAuthNodeServiceImpl implements IAuthNodeService {

    private final IAuthNodeDbService mDepartmentDbService;
    private final ICompanyDbService mCompanyDbService;
    private final IDeviceGroupDbService mDeviceGroupDbService;
    private final IDeptDbService mDeptDbService;


    public AuthNodeServiceImpl(IBaseAuthNodeRelevantDao pDepartmentRelevantDao,
                               IAuthNodeDbService mDepartmentDbService,
                               ICompanyDbService pCompanyDbService,
                               IDeviceGroupDbService pDeviceGroupDbService,
                               IDeptDbService pDeptDbService){
        super(pDepartmentRelevantDao);
        this.mDepartmentDbService = mDepartmentDbService;
        mCompanyDbService = pCompanyDbService;
        mDeviceGroupDbService = pDeviceGroupDbService;
        mDeptDbService = pDeptDbService;
    }

    @Override
    protected void onInit(String pDomain, List<AuthNodeCfg> pAuthNodeCfgList, List<BaseAuthNodeEntity> pSavingBaseAuthNodeEntityList) {
        assert(pAuthNodeCfgList.size() == pSavingBaseAuthNodeEntityList.size());

        List<DeviceGroupEntity> deviceGroupEntityList = new ArrayList<>();
        List<CompanyEntity> companyEntityList = new ArrayList<>();
        List<DeptEntity> deptEntityList = new ArrayList<>();

        for(int i=0;i<pAuthNodeCfgList.size();i++){
            AuthNodeCfg authNodeCfg = pAuthNodeCfgList.get(i);
            if("rt".equals(authNodeCfg.getCode())){
                continue;
            }

            BaseAuthNodeEntity baseAuthNodeEntity = pSavingBaseAuthNodeEntityList.get(i);
            JSONObject jsonObject = authNodeCfg.getCfg();
            switch (pDomain){
                case "zf-mgr":
                    DeviceGroupEntity deviceGroupEntity = DbDtoEntityUtil.createFromDto(jsonObject,DeviceGroupEntity.class);
                    deviceGroupEntity.setAuthId(baseAuthNodeEntity.getId());
                    deviceGroupEntityList.add(deviceGroupEntity);
                    break;
                case "zf-merchant":
                    CompanyEntity companyEntity = DbDtoEntityUtil.createFromDto(jsonObject,CompanyEntity.class);
                    companyEntity.setAuthId(baseAuthNodeEntity.getId());
                    companyEntityList.add(companyEntity);
                    break;
                case "zf-account":
                    DeptEntity deptEntity = DbDtoEntityUtil.createFromDto(jsonObject,DeptEntity.class);
                    deptEntity.setAuthId(baseAuthNodeEntity.getId());
                    deptEntityList.add(deptEntity);
                    break;
            }
        }
        mDeviceGroupDbService.saveBatch(deviceGroupEntityList);
        mCompanyDbService.saveBatch(companyEntityList);
        mDeptDbService.saveBatch(deptEntityList);
    }

    @Override
    protected void onDepCodeChange(BaseAuthNodeEntity pBaseAuthNodeEntity) {

    }

    @Override
    protected void onDepDelete(BaseAuthNodeEntity pBaseAuthNodeEntity) {
        mDeviceGroupDbService.deleteByAuthId(pBaseAuthNodeEntity.getId());
        mCompanyDbService.deleteById(pBaseAuthNodeEntity.getId());
        mDeptDbService.deleteByAuthId(pBaseAuthNodeEntity.getId());
    }

    @Override
    public AuthNodeEntity create(Long pParentId, DepartmentCreateDto pDepartmentCreateDto) {
        AuthNodeEntity authNodeEntity = DbDtoEntityUtil.createFromDto(pDepartmentCreateDto, AuthNodeEntity.class);
        return (AuthNodeEntity)super.create(pParentId, authNodeEntity);
    }

    @Override
    public AuthNodeEntity edit(Long pId, DepartmentEditDto pDepartmentEditDto) {
        AuthNodeEntity orgAuthNodeEntity = mDepartmentDbService.check(pId);
        AuthNodeEntity newAuthNodeEntity = DbDtoEntityUtil.editByDto(orgAuthNodeEntity,pDepartmentEditDto, AuthNodeEntity.class);
        newAuthNodeEntity = mDepartmentDbService.updatePull(newAuthNodeEntity.getId(), newAuthNodeEntity);
        return newAuthNodeEntity;
    }

}

4.5 初始化用户

基类实现

@Slf4j
@RequiredArgsConstructor
public abstract class BaseUserServiceImpl implements IBaseUserService {

    protected final IBaseUserRelevantDao mUserRelevantDao;
    protected final IBaseAuthNodeRelevantDao mDepartmentRelevantDao;
    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());
    }

    protected BaseUserEntity _createUser(BaseUserEntity pBaseUserEntity, List<String> pRoles, Map<String,String> pIniDeps) {
        if (mUserRelevantDao.user_existByName(pBaseUserEntity.getName())) {
            throw new ServiceException("已经存在名为" + pBaseUserEntity.getName() + "的用户");
        }
        PasswdConfig passwdConfig = mSecurityConfig.getPasswd();
        UserConfig userConfig = mSecurityConfig.getUserConfig();
        BaseUserEntity userEntity = pBaseUserEntity;

        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 = null;
            baseRoleEntity = mUserRelevantDao.role_check(roleCode);
            baseRelUserRoleEntity.setRoleId(baseRoleEntity.getId());
            baseRelUserRoleEntity.setRoleCode(baseRoleEntity.getCode());
            baseRelUserRoleEntityList.add(baseRelUserRoleEntity);
        }
        mUserRelevantDao.relUserRole_updateRoles(userEntity.getId(), baseRelUserRoleEntityList);

        // 设置初始的部门
        List<BaseRelUserAuthEntity> savingRelUserDepEntity = new ArrayList<>();
        List<DepartmentConfig> defaultDepartmentConfigList = null;

        if(null != pIniDeps){
            defaultDepartmentConfigList = pIniDeps.entrySet().stream().map(entry->{
                DepartmentConfig departmentConfig = new DepartmentConfig();
                departmentConfig.setDomain(entry.getKey());
                departmentConfig.setDepCode(entry.getValue());
                return departmentConfig;
            }).collect(Collectors.toList());
        }else{
            defaultDepartmentConfigList = userConfig.getDefaultDepartments();
        }

        for (int i = 0; i < defaultDepartmentConfigList.size(); i++) {
            DepartmentConfig departmentConfig = defaultDepartmentConfigList.get(i);
            BaseRelUserAuthEntity baseRelUserAuthEntity = mDepartmentRelevantDao.relUserAuth_generate();
            baseRelUserAuthEntity.createInit();
            baseRelUserAuthEntity.setUserId(userEntity.getId());
            baseRelUserAuthEntity.setUserName(userEntity.getName());
            baseRelUserAuthEntity.setDomainCode(departmentConfig.getDomain());
            BaseDomainVo baseDomainVo = null;
            BaseDomainEntity domainEntity  = mDepartmentRelevantDao.domain_check(departmentConfig.getDomain());
            baseRelUserAuthEntity.setDomainId(domainEntity.getId());
            String depCode = departmentConfig.getDepCode();
            BaseAuthNodeEntity departmentEntity = null;
            if(null != baseDomainVo){
                departmentEntity = baseDomainVo.checkDepartment(depCode);
            }else{
                departmentEntity = mDepartmentRelevantDao.authNode_check(domainEntity.getId(), depCode);
            }
            baseRelUserAuthEntity.setAuthId(departmentEntity.getId());
            baseRelUserAuthEntity.setAuthCode(departmentEntity.getCode());
            baseRelUserAuthEntity.setAuthCascade(departmentEntity.getCascade());
            savingRelUserDepEntity.add(baseRelUserAuthEntity);
        }
        mDepartmentRelevantDao.relUserAuth_saveBatch(userEntity.getId(), savingRelUserDepEntity);
        return userEntity;
    }
    
    @Transactional(rollbackFor = Exception.class)
    @Override
    public BaseUserEntity createUser(BaseUserEntity pBaseUserEntity, List<String> pRoles, Map<String,String> pIniDeps) {
        return _createUser(pBaseUserEntity, pRoles, pIniDeps);
    }
    
    @Transactional(rollbackFor = Exception.class)
    public List<BaseUserEntity> initUser(JSONArray pDefaultUserCfg) {
        List<BaseUserEntity> userEntityList = new ArrayList<>();
        for (int i = 0; i < pDefaultUserCfg.size(); i++) {
            JSONObject userJsonCfg = pDefaultUserCfg.getJSONObject(i);
            List<String> roles = userJsonCfg.getList("roles", String.class);
            Map<String,String> deps = (Map<String,String>)userJsonCfg.get("departments");
            BaseUserEntity baseUserEntity = mUserRelevantDao.user_load(userJsonCfg);
            if (!mUserRelevantDao.user_existByName(baseUserEntity.getName())) {
                baseUserEntity = _createUser(baseUserEntity, roles, deps);
            }
            userEntityList.add(baseUserEntity);
        }
        return userEntityList;
    }

	/*********************还有其他逻辑,省略**************************/
}

4.6 初始化设备

@RequiredArgsConstructor
@Slf4j
@ZfFacade(name = "设备")
public class DeviceServiceImpl implements IDeviceService {

    private final IDeviceDbService mDeviceDbService;
    private final IAuthNodeRelevantDao mDepartmentRelevantDao;
    private final ICompanyDbService mCompanyDbService;
    private final IUserDbService mUserDbService;
    private final ITokenUtil mTokenUtil;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void init() {
        ClassPathResource classPathResource = new ClassPathResource("device.json");
        byte[] configData = null;
        try {
            InputStream inputStream = classPathResource.getInputStream();
            configData = inputStream.readAllBytes();
        } catch (Exception ex) {
            log.error("缺失配置文件 " + "device.json "+ex);
            return;
        }
        JSONObject deviceInitConfigData = JSON.parseObject(configData);
        JSONArray deviceInitConfigArray = deviceInitConfigData.getJSONArray("data");
        List<DeviceEntity> savingDeviceList = new ArrayList<>();
        List<DeviceEntity> editingDeviceList = new ArrayList<>();
        DomainEntity domainEntity = mDepartmentRelevantDao.domain_check("zf-mgr");
        List<DeviceInitDto> deviceInitDtoList = new ArrayList<>();
        Set<String> deviceCodeSet = new HashSet<String>();
        for(int i=0;i<deviceInitConfigArray.size();i++){
            DeviceInitDto deviceInitDto = deviceInitConfigArray.getObject(i,DeviceInitDto.class);
            deviceInitDtoList.add(deviceInitDto);
            if(deviceCodeSet.contains(deviceInitDto.getCode())){
                throw new ServiceException("设备码"+deviceInitDto.getCode()+"重复");
            }
            deviceCodeSet.add(deviceInitDto.getCode());
        }

        for(DeviceInitDto deviceInitDto : deviceInitDtoList){
            DeviceEntity orgDeviceEntity = findByCode(deviceInitDto.getCode());
            AuthNodeEntity authNodeEntity = mDepartmentRelevantDao.authNode_check(domainEntity.getId(),deviceInitDto.getDepCode());
            if(null != orgDeviceEntity){
                if(!orgDeviceEntity.getGroupAuthId().equals(authNodeEntity.getId())){
                    orgDeviceEntity.setGroupAuthId(authNodeEntity.getId());;
                    orgDeviceEntity.setGroupAuthCascade(authNodeEntity.getCascade());
                }
                DeviceEntity editingDeviceEntity = DbDtoEntityUtil.editByDto(orgDeviceEntity,deviceInitDto,DeviceEntity.class);
                editingDeviceList.add(editingDeviceEntity);
            }else{
                DeviceEntity newDeviceEntity = DbDtoEntityUtil.createFromDto(deviceInitDto,DeviceEntity.class);
                newDeviceEntity.setGroupAuthId(authNodeEntity.getId());
                newDeviceEntity.setGroupAuthCascade(authNodeEntity.getCascade());
                String companyName = deviceInitDto.getCompanyName();
                CompanyEntity companyEntity = mCompanyDbService.findByName(companyName);
                if(null != companyEntity){
                    newDeviceEntity.setCompanyAuthId(companyEntity.getAuthId());
                    newDeviceEntity.setCompanyName(companyEntity.getName());
                }else{
                    newDeviceEntity.setCompanyAuthId(-1L);
                    newDeviceEntity.setCompanyName("未定义");
                }
                String createUser = deviceInitDto.getCreateUser();
                Long userId = mUserDbService.findByUserName(createUser);
                if(null != userId){
                    UserEntity userEntity = mUserDbService.check(userId);
                    newDeviceEntity.setCreateBy(userEntity.getId());
                }
                savingDeviceList.add(newDeviceEntity);
            }
        }
        mDeviceDbService.saveBatch(savingDeviceList);
        mDeviceDbService.updateBatchById(editingDeviceList);
    }
}

五、后记

至此,该鉴权的核心代码已基本放出,由于本篇篇幅过长,打算在下一篇中,展示如何使用本篇所讲权限注解。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值