一、权限的设计
在芝法酱躺平攻略(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);
}
}
五、后记
至此,该鉴权的核心代码已基本放出,由于本篇篇幅过长,打算在下一篇中,展示如何使用本篇所讲权限注解。