上篇文章的数据模型是基于传统的RBAC模型来设计的,由于我们这里的应用场景不一样,所以这里的数据权限模型并没有严格按照上篇文章的方案来设计,但是万变不离其宗,核心原理还是相同的。
首先我来介绍一下我们最终实现的效果

一个组件(可以理解成菜单)可以绑定多个授权维度,当给角色授权组件时可以给这个授权组件赋予不同维度的权限。
关于数据权限的授权维度有以下几个关键点需要仔细体会:
where 产品线 in ('A产品线')where 客户群 in('A客户群')AND 产品线 in ('B产品线')where 客户群 in ('A客户群','B客户群') OR 产品线 in ('A产品线')当然我们业务场景中数据规则比较单一,全部使用
in作为sql条件连接符,你们可以根据实际业务场景进行补充。
最终的数据模型如下所示:

这里的组件大家完全可以理解成RBAC模型中的资源、菜单,只不过叫法不同而已。
下面是具体的表结构设计

DataPermission@DataPermission
String componentRoute() default “”;
}
@Autowired
private RoleComponentRuleService roleComponentRuleService;
@Pointcut(“@annotation(com.ifly.workbench.security.annotation.DataPermission)”)
public void pointCut() {
}
@Around(“pointCut()”)
public Object around(ProceedingJoinPoint point) throwsThrowable{
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
//获取请求token
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String userName = JwtUtil.getUsername(token);
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataPermission permissionData = method.getAnnotation(DataPermission.class);
//获取授权方式
String permissionType = permissionData.permissionType();
//获取组件路由
String componentRoute = permissionData.componentRoute();
if (StringUtils.isNotEmpty(componentRoute)){
// 查找当前用户此组件下的所有规则
List<RoleComponentRuleDTO> componentRules = roleComponentRuleService.getRoleComponentRule(userName, componentRoute);
if(CollectionUtils.isNotEmpty(componentRules)){
DataPermissionUtils.installDataSearchConditon(request, componentRules);
SysUserCacheInfo userInfo = buildCacheUser(userName);
DataPermissionUtils.installUserInfo(request, userInfo);
}
}
returnpoint.proceed();
}
private SysUserCacheInfo buildCacheUser(String userName) {
SysUserCacheInfo info = new SysUserCacheInfo();
info.setSysUserName(userName);
info.setOneDepart(true);
return info;
}
}
在AOP中获取当前用户、需要访问的组件中所有的数据规则,参考wb_role_component_rule表设计,并将其放到Request作用域中。
public static final String SYS_USER_INFO = “SYS_USER_INFO”;
public static void installDataSearchConditon(HttpServletRequest request, List<RoleComponentRuleDTO> componentRules) {
// 1.先从request获取MENU_DATA_AUTHOR_RULES,如果存则获取到LIST
List<RoleComponentRuleDTO> list = loadDataSearchCondition();
if (list==null) {
// 2.如果不存在,则new一个list
list = Lists.newArrayList();
}
list.addAll(componentRules);
// 3.往list里面增量存指
request.setAttribute(COMPONENT_DATA_RULES, list);
}
@SuppressWarnings(“unchecked”)
public synchronized List<RoleComponentRuleDTO> loadDataSearchCondition() {
return (List<RoleComponentRuleDTO>) SpringContextUtils.getHttpServletRequest().getAttribute(COMPONENT_DATA_RULES);
}
public synchronized void installUserInfo(HttpServletRequest request, SysUserCacheInfo userinfo) {
request.setAttribute(SYS_USER_INFO, userinfo);
}
}
在Request中存储数据规则。
List<RoleComponentRuleDTO> getRoleComponentRule(String userName, String componentCode);
}
@Resource
private RoleComponentRuleMapper roleComponentRuleMapper;
@Override
public List<RoleComponentRuleDTO> getRoleComponentRule(String userName, String componentCode) {
return roleComponentRuleMapper.getRoleComponentRule(userName,componentCode);
}
}
private static final String SQL_AND = ” and “;
private static final String SQL_OR = ” or “;
private static final String SQL_JOINT = ” (%s) “;
public String getPermissionSql(String identityId) {
//————————获取当前身份的数据规则————————————
List<RoleComponentRuleDTO> conditionList = getCurrentIdentyPermission(identityId);
if (CollectionUtils.isEmpty(conditionList)) {
//没有权限
return “1 = 0”;
}
//存在权限
//对当前身份根据规则编码分组-去除不同角色中相同编码且规则值为ALL的规则 并根据角色id分组
Map<String, List<RoleComponentRuleDTO>> ruleMap = getRuleMapByRoleId(conditionList);
StringBuilder sb = new StringBuilder();
String roleSql;
if (MapUtils.isNotEmpty(ruleMap)) {
//按角色拼接SQL
for (Map.Entry<String, List<RoleComponentRuleDTO>> entry : ruleMap.entrySet()) {
List<RoleComponentRuleDTO> lists = entry.getValue();
// 同角色之间使用 AND
roleSql = buildRoleSql(lists);
//角色之间使用 OR
if (StringUtils.isNotEmpty(roleSql)) {
jointSqlByRoles(sb, roleSql);
}
}
}
return sb.toString();
}
private static List<RoleComponentRuleDTO> getCurrentIdentyPermission(String identityId) {
//—————————-获取所有数据规则—————————–
List<RoleComponentRuleDTO> roleRuleList = DataPermissionUtils.loadDataSearchCondition();
if(CollectionUtils.isEmpty(roleRuleList)){
return null;
}
//—————————–过滤掉不属于当前身份的规则———————————–
return roleRuleList.stream()
.filter(item -> item.getIdentityId().equals(identityId))
.collect(Collectors.toList());
}
private static String buildRoleSql(List<RoleComponentRuleDTO> lists) {
StringBuilder roleSql = new StringBuilder();
for (RoleComponentRuleDTO item : lists) {
//如果出现全选 则 代表全部,不需要限定范围
if (“ALL”.equals(item.getRuleValue())) {
continue;
}
//将规则转换成SQL
String filedSql = convertRuleToSql(item);
roleSql.append(SQL_AND).append(filedSql);
}
return roleSql.toString();
}
private static String convertRuleToSql(RoleComponentRuleDTO rule) {
String whereCondition = ” in “;
String ruleValueConvert = getInConditionValue(rule.getRuleValue());
return rule.getRuleCode() + whereCondition + ruleValueConvert;
}
private static String getInConditionValue(String ruleValue) {
String[] temp = ruleValue.split(“,”);
StringBuilder res = new StringBuilder();
for (String string : temp) {
res.append(“,'”).append(string).append(“‘”);
}
return “(” + res.substring(1) + “)”;
}
private static void jointSqlByRoles(StringBuilder sqlBuilder, String roleSql) {
roleSql = roleSql.replaceFirst(SQL_AND, “”);
if (StringUtils.isEmpty(sqlBuilder.toString())) {
sqlBuilder.append(String.format(SQL_JOINT, roleSql));
} else {
sqlBuilder.append(SQL_OR).append(String.format(SQL_JOINT, roleSql));
}
}
private static Map<String, List<RoleComponentRuleDTO>> getRuleMapByRoleId(List<RoleComponentRuleDTO> conditionList) {
//——–过滤掉不属于当前身份的规则,并对条件编码进行分组———————————–
Map<String, List<RoleComponentRuleDTO>> conditionMap = conditionList.stream().collect(Collectors.groupingBy(RoleComponentRuleDTO::getRuleCode));
//——–相同编码分组中存在ALL的排除掉———————————————–
List<RoleComponentRuleDTO> newRoleRuleList = new ArrayList<>();
if (MapUtils.isNotEmpty(conditionMap)) {
for (Map.Entry<String, List<RoleComponentRuleDTO>> entry : conditionMap.entrySet()) {
boolean flag = true;
List<RoleComponentRuleDTO> lists = entry.getValue();
for (RoleComponentRuleDTO item : lists) {
if (“ALL”.equals(item.getRuleValue())) {
flag = false;
break;
}
}
if (flag) {
newRoleRuleList.addAll(lists);
}
}
}
if (CollectionUtils.isNotEmpty(newRoleRuleList)) {
return newRoleRuleList.stream().collect(Collectors.groupingBy(RoleComponentRuleDTO::getRoleId));
}
return Maps.newHashMap();
}
}
核心类,用于生成数据权限查询的SQL脚本。
以上,就是数据权限的实现过程,其实代码实现并不复杂,主要还是得理解其中的实现原理。如果你也有数据权限的需求,不妨参考一下。
到此这篇关于MySQL数据权限的实现详情的文章就介绍到这了,更多相关SQL数据权限内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
发表评论