目录

SpringSecurity权限管理系统实战—一、项目简介和开发环境准备

SpringSecurity权限管理系统实战—二、日志、接口文档等实现

SpringSecurity权限管理系统实战—三、主要页面及接口实现

SpringSecurity权限管理系统实战—四、整合SpringSecurity(上)

SpringSecurity权限管理系统实战—五、整合SpringSecurity(下)

SpringSecurity权限管理系统实战—六、SpringSecurity整合jwt

SpringSecurity权限管理系统实战—七、处理一些问题

SpringSecurity权限管理系统实战—八、AOP 记录用户日志、异常日志

SpringSecurity权限管理系统实战—九、数据权限的配置

前言

这一章的部分是我写到现在最累的一部分,累就累在逻辑的处理上,因为涉及到很多多表的操作,幸好数据库没有设计外键,不然更加恶心。

也让我发现了数据库设计之初的一些命名问题(之后再解决这个问题)。

就像是用户表中的id最好还是用user_id,而我之前用的却是id。这会导致什么问题呢?比如说你在role_user这张关联表中存的肯定是user_id吧,那么你在关联这两个表写sql语句是就要不停的转换id和user_id,脑壳子都给你绕晕了。血淋淋的教训,大家要注意了。

这篇文章只是给一个思路,内容和逻辑都太复杂,还要对原有的部分进行修改,不能再像之前那样一步一步贴代码了

希望各位小伙伴能够多多star支持,您的点赞就是我维护的动力

giteegithub中同步更新源码

附:阿里巴巴数据库设计规范

一、什么是数据权限

权限设计具体来说可以分为功能权限和数据权限。功能权限就是角色能操作哪些接口,而数据权限就是角色能够获取到的哪些数据。

形象点来说,如果现在有一个公司,公司上下有很多部门,部门里有很多员工,而数据权限就是为了让某个部门的人只能获取到自己部门或着是指定部门的员工信息。

二、新建如下表







分别是岗位表,部门表,用户岗位关联表和角色部门关联表

my_user表中添加dept_id字段。my_role表中添加data_scpoe字段。前一个很好理解,就是部门的id,后一个代表的就是角色的数据权限范围

(这篇文章很难写,文章所代表的代码内容也很难写,大家想要理解透彻还是要从源码里看,自己做一遍才能真正的理解)

三、效果

效果

效果就是这样,代码也不贴了,这部分逻辑有点复杂,代码量很大,可以自行去源码中查看。

四、实现

这里只是介绍下如何实现数据权限,参考了若依项目中的实现方法

首先我们自定义一个注解

/**
* 数据权限过滤注解
* @author codermy
* @createTime 2020/8/22
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {
/**
* 部门表的别名
*/
public String deptAlias() default ""; /**
* 用户表的别名
*/
public String userAlias() default "";
}

再定义一个切面类

	/**
* 数据过滤处理
* @author codermy
* @createTime 2020/8/22
*/
@Aspect
@Component
public class DataScopeAspect { @Autowired
public RoleUserService roleUserService;
/**
* 全部数据权限
*/
public static final String DATA_SCOPE_ALL = "1"; /**
* 自定数据权限
*/
public static final String DATA_SCOPE_CUSTOM = "2"; /**
* 部门数据权限
*/
public static final String DATA_SCOPE_DEPT = "3"; /**
* 部门及以下数据权限
*/
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; /**
* 仅本人数据权限
*/
public static final String DATA_SCOPE_SELF = "5"; /**
* 数据权限过滤关键字
*/
public static final String DATA_SCOPE = "dataScope"; /**
* 配置织入点
*/
@Pointcut("@annotation(com.codermy.myspringsecurityplus.admin.annotation.DataPermission)")
public void dataScopePointCut()
{
} @Before("dataScopePointCut()")
public void doBefore(JoinPoint point) throws Throwable
{
handleDataScope(point);
} protected void handleDataScope(final JoinPoint joinPoint)
{
// 获得注解
DataPermission controllerDataScope = getAnnotationLog(joinPoint);
if (controllerDataScope == null)
{
return;
}
// 获取当前的用户
JwtUserDto currentUser = SecurityUtils.getCurrentUser();
if (currentUser != null)
{
// 如果是超级管理员,则不过滤数据
if (!currentUser.isAdmin())
{
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias());
}
}
} /**
* 数据范围过滤
*
* @param joinPoint 切点
* @param user 用户
* @param deptAlias 部门别名
* @param userAlias 用户别名
*/
public static void dataScopeFilter(JoinPoint joinPoint, JwtUserDto user, String deptAlias, String userAlias)
{
StringBuilder sqlString = new StringBuilder(); for (MyRole role : user.getRoleInfo())
{
String dataScope = role.getDataScope();
if (DATA_SCOPE_ALL.equals(dataScope))
{
sqlString = new StringBuilder();
break;
}
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
{
sqlString.append(StrUtil.format(
" OR {}.id IN ( SELECT dept_id FROM my_role_dept WHERE role_id = {} ) ", deptAlias,
role.getId()));
}
else if (DATA_SCOPE_DEPT.equals(dataScope))
{
sqlString.append(StrUtil.format(" OR {}.id = {} ", deptAlias, user.getMyUser().getDeptId()));
}
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
{
sqlString.append(StrUtil.format(
" OR {}.id IN ( SELECT id FROM my_dept WHERE id = {} or find_in_set( {} , ancestors ) )",
deptAlias, user.getMyUser().getDeptId(), user.getMyUser().getDeptId()));
}
else if (DATA_SCOPE_SELF.equals(dataScope))
{
if (StrUtil.isNotBlank(userAlias))
{
sqlString.append(StrUtil.format(" OR {}.id = {} ", userAlias, user.getMyUser().getId()));
}
else
{
// 数据权限为仅本人且没有userAlias别名不查询任何数据
sqlString.append(" OR 1=0 ");
} }
}
if (StrUtil.isNotBlank(sqlString.toString()))
{
BaseEntity baseEntity;
for (int i = 0;i < joinPoint.getArgs().length ;i++ ){
if (joinPoint.getArgs()[i] instanceof BaseEntity){
baseEntity= (BaseEntity) joinPoint.getArgs()[i];
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
}
} }
} /**
* 是否存在注解,如果存在就获取
*/
private DataPermission getAnnotationLog(JoinPoint joinPoint)
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod(); if (method != null)
{
return method.getAnnotation(DataPermission.class);
}
return null;
}
}

解释一下这个切面类的作用就是:

当你在ServiceImpl的某个方法上定义了这个注解时,它就会获取当前登录用户的角色的dataScope(就是数据范围),然后比较它的值,将相应的sql语句存入baseEntity的params中

然后我们只需要在对于需要配置数据权限的mapper.xml中添加${params.dataScope}



再在调用此方法的service方法上添加注解@DataPermission(deptAlias = "d", userAlias = "u")

即可



这里我在BaseEntity中添加了一个params属性来用于存放数据权限的sql



这样当我们需要调用这个sql时,如果未过滤权限时,sql是这样的

SELECT u.id, u.dept_id, u.user_name, u.password, u.nick_name
, u.phone, u.email, u.status, u.create_time, u.update_time
FROM my_user u
LEFT JOIN my_dept d ON u.dept_id = d.id
WHERE u.dept_id = ?
OR u.dept_id IN (
SELECT e.id
FROM my_dept e
WHERE FIND_IN_SET(?, ancestors)
)
ORDER BY u.id

如果我们给这个角色的数据权限种类是自定数据权限,sql就会是下面这样

SELECT u.id, u.dept_id, u.user_name, u.password, u.nick_name
, u.phone, u.email, u.status, u.create_time, u.update_time
FROM my_user u
LEFT JOIN my_dept d ON u.dept_id = d.id
WHERE (u.dept_id = ?
OR u.dept_id IN (
SELECT e.id
FROM my_dept e
WHERE FIND_IN_SET(?, ancestors)
))
AND d.id IN (
SELECT dept_id
FROM my_role_dept
WHERE role_id = 2
)
ORDER BY u.id

那么如果我们给角色 ‘普通用户’如下数据权限



那么当我们登录该角色的用户时,便只能访问到相应部门下的用户信息。





当我们再在部门的相应方法和sql上再添加上注解时过滤语句时,那么效果就是这样的



giteegithub中同步更新源码

SpringSecurity权限管理系统实战—九、数据权限的配置的更多相关文章

  1. SpringSecurity权限管理系统实战—一、项目简介和开发环境准备

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  2. SpringSecurity权限管理系统实战—二、日志、接口文档等实现

    系列目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战 ...

  3. SpringSecurity权限管理系统实战—四、整合SpringSecurity(上)

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  4. SpringSecurity权限管理系统实战—六、SpringSecurity整合jwt

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  5. SpringSecurity权限管理系统实战—七、处理一些问题

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  6. SpringSecurity权限管理系统实战—八、AOP 记录用户、异常日志

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  7. SpringSecurity权限管理系统实战—五、整合SpringSecurity(下)

    系列目录 前言 上篇文章SpringSecurity整合了一半,这次把另一半整完,所以本篇的序号接着上一篇. 七.自定义用户信息 前面我们登录都是用的指定的用户名和密码或者是springsecurit ...

  8. Smartbi权限安全管理系统_保障数据权限安全

    思迈特软件Smartbi具有完善的安全管理体系,Smartbi权限安全管理系统它可以控制用户功能权限.数据访问权限.资源访问权限.Smartbi权限安全管理系统支持按用户.用户组.角色进行管理:支持多 ...

  9. 08 SSM整合案例(企业权限管理系统):08.权限控制

    04.AdminLTE的基本介绍 05.SSM整合案例的基本介绍 06.产品操作 07.订单操作 08.权限控制 09.用户操作 10.权限关联与控制 11.AOP日志 08.权限控制 SSM权限操作 ...

随机推荐

  1. 记node,elementui,formidable上传图片

    卡了很久甚至心态爆炸,搞了半天才能成功上传图片,结果图片上传的位置也有问题.头大 https://www.jianshu.com/p/3a61318c24d1 先是参考这个来弄好大致的部署. 然后中途 ...

  2. Python打印到屏幕_读取键盘输入

    Python打印到屏幕_读取键盘输入: print( ): 打印输出括号中的值 print("hello") # hello strs = 'hello' print(" ...

  3. Python os.isatty() 方法

    概述 os.isatty() 方法用于判断如果文件描述符fd是打开的,同时与tty(-like)设备相连,则返回true, 否则False.高佣联盟 www.cgewang.com 语法 isatty ...

  4. PHP asinh() 函数

    实例 返回不同数的反双曲正弦: <?phpecho(asinh(7) . "<br>");echo(asinh(56) . "<br>&qu ...

  5. PHP number_format() 函数

    实例 格式化数字: <?php高佣联盟 www.cgewang.comecho number_format("1000000")."<br>" ...

  6. Sharding-JDBC实现读写分离

    参考资料:猿天地  https://mp.weixin.qq.com/s/kp2lJHpTMz4bDWkJYjVbOQ  作者:尹吉欢 技术选型:SpringBoot + Sharding-JDBC ...

  7. MyBatis-Plus使用(2)-CRUD接口

    参考文档:https://mybatis.plus/guide/crud-interface.html MyBatis-Plus自带的CRUD方法分为Mapper层和Service层,大多数功能是重叠 ...

  8. MySQL进阶篇(03):合理的使用索引结构和查询

    本文源码:GitHub·点这里 || GitEE·点这里 一.高性能索引 1.查询性能问题 在MySQL使用的过程中,所谓的性能问题,在大部分的场景下都是指查询的性能,导致查询缓慢的根本原因是数据量的 ...

  9. java进阶(2)--抽象类

    一.抽象类集刺虎: 1.概念 类与类之前有共同特征,将这些共同特征提取出来,行为抽象类   2.抽象类的数据类型: 引用数据类型   3.语法 [修饰符列表]abstract Class 类型{类体} ...

  10. .NET 异步详解

    前言 博客园中有很多关于 .NET async/await 的介绍,但是很遗憾,很少有正确的,甚至说大多都是"从现象编原理"都不过分. 最典型的比如通过前后线程 ID 来推断其工作 ...