目录

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. activiti7 获取流程定义的xml

    RepositoryService repositoryService = ProcessEngines.getDefaultProcessEngine().getRepositoryService( ...

  2. hibernate自动创建表报错,提示不存在

    报错:ERROR: HHH000299: Could not complete schema update  或 不能执行statement等 解决方式: 根据mysql版本更改hibernate.c ...

  3. GitLab 系列文章

    GitLab 系列文章 记录 GitLab 的相关文章 列表 Docker 搭建 GitLab GitLab CI/CD 配置 GitLab 配置模板 访问 GitLab 数据库 GitLab 转让所 ...

  4. C#LeetCode刷题之#191-位1的个数(Number of 1 Bits)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4052 访问. 编写一个函数,输入是一个无符号整数,返回其二进制表 ...

  5. C#LeetCode刷题之#441-排列硬币(Arranging Coins)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3995 访问. 你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状 ...

  6. NeuralCoref: python的共指消解工具教程

    转载地址 https://blog.csdn.net/blmoistawinde/article/details/81782971 共指消解 首先简要地说说共指消解是什么,有什么用处.假设机器正在阅读 ...

  7. HahMap(jdk=1.8)源码解读

    简介:岁月磨平了人的棱角,让我们不敢轻易的去放手,即使它在你心中并不那么重要,你依旧害怕失去它,不是舍不得,是内心的迷茫. 一 : 创建HashMap HashMap<Object, Objec ...

  8. python 03 常用操作符

    1. e记法,科学计数法. AeB   A,B为整数,A*10的B次方. 2. 逻辑运算,真为1,假为0,最好不使用这个计算 true(1)    false(0) true+true=2 3.类型转 ...

  9. python 02 if while

    1. if的格式 >>> 1<3 True 真>>> 1>3False 假 if   条件:                     条件 + : (t ...

  10. servlet的生命周期和工作原理介绍

    一.servlet生命周期 Servlet生命周期分为三个阶段: 1)初始化阶段: 调用init()方法 2)响应客户请求阶段:调用service()方法 3)终止阶段:调用destroy()方法 T ...