基于AOP方式实现数据权限的校验
本文大纲
一、数据权限注解
二、数据权限AOP
三、数据范围枚举
四、登录用户信息
五、权限相关的工具类
六、AOP数据权限校验基础类
七、AOP数据权限校验基础类
一、数据权限注解
package com.dashu.park.common.annotation;
import com.dashu.park.common.enums.DataScopeType;
import java.lang.annotation.*;
/**
* 数据权限过滤注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope {
/**
* 是否校验角色,没有角色不然操作
*
* @return
*/
boolean checkRole() default true;
/**
* 是否校验数据权限,没有权限,直接返回
*
* @return
*/
boolean checkDataScope() default false;
/**
* 数据权限范围的类型,默认是本部门的数据
*
* @return
*/
DataScopeType dataScopeType() default DataScopeType.DATA_SCOPE_DEPT;
}
二、数据权限AOP
package com.dashu.park.common.aspect;
import cn.hutool.core.util.ObjectUtil;
import com.dashu.park.common.annotation.DataScope;
import com.dashu.park.common.bean.DataScopeAopEntity;
import com.dashu.park.common.bean.LoginUser;
import com.dashu.park.common.enums.DataScopeType;
import com.dashu.park.common.exception.BaseException;
import com.dashu.park.common.util.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Optional;
@Aspect
@Component
public class DataScopeAspect {
/**
* 配置织入点
*/
@Pointcut("@annotation(com.dashu.park.common.annotation.DataScope)")
public void dataScopePointCut() {
}
@Before("dataScopePointCut()")
public void before(JoinPoint point) {
// handleDataScope(point);
}
// protected void handleDataScope(final JoinPoint joinPoint) {
// // 获得注解
// DataScope dataScope = getAnnotationLog(joinPoint);
// if (ObjectUtil.isNull(dataScope)) {
// return;
// }
// // 获取当前的用户
// LoginUser loginUser = SecurityUtils.getLoginUser();
//
// // 如果是超级管理员,则不过滤数据
// if (SecurityUtils.isAdmin(loginUser)) {
// return;
// }
//
// // 1.角色判断
// if (dataScope.checkRole()) {
// SecurityUtils.checkRole(loginUser);
// }
//
// // 2.权限判断,有权限才执行
// if (dataScope.checkDataScope()) {
// // 校验是否有权限
// SecurityUtils.checkDataScope(loginUser);
// // 加入查询的规则
// // 拿到方法的参数,要求第一个参数为实体类且继承 DataScopeAopEntity
// // 因为要将 DataScopeAopEntity 的属性设置为查询条件
// Object params = joinPoint.getArgs()[0];
//
// DataScopeType dataScopeType = dataScope.dataScopeType();
//
// // 转为权限类型,向上转型
// DataScopeAopEntity dataScopeAop = (DataScopeAopEntity) params;
// // 基于数据权限的类型,设置用户的权限值
// dataScopeType.fillDataScopeType(dataScopeAop, loginUser.getDeptIds());
// }
// }
/**
* 数据范围过滤
* <p>
* 是否存在注解,如果存在就获取
*/
private DataScope getAnnotationLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
return Optional.ofNullable(method).map(item -> method.getAnnotation(DataScope.class)).orElseGet(() -> null);
}
/**
* 定制一个环绕通知
*
* @param joinPoint
* @throws Throwable
*/
@Around("dataScopePointCut()")
public Object advice(ProceedingJoinPoint joinPoint) throws Throwable {
try {
// 获得注解
DataScope dataScope = this.getAnnotationLog(joinPoint);
if (ObjectUtil.isNull(dataScope)) {
return joinPoint.proceed();
}
// 获取当前的用户
LoginUser loginUser = SecurityUtils.getLoginUser();
// 如果是超级管理员,则不过滤数据
if (SecurityUtils.isAdmin(loginUser)) {
// 执行proceed方法的作用是让目标方法执行
return joinPoint.proceed();
}
// 1.角色判断
if (dataScope.checkRole()) {
SecurityUtils.checkRole(loginUser);
}
// 2.权限判断,有权限才执行
if (dataScope.checkDataScope()) {
// 校验是否有权限
SecurityUtils.checkDataScope(loginUser);
// 加入查询的规则
// 拿到方法的参数,要求第一个参数为实体类且继承 DataScopeAopEntity
// 因为要将 DataScopeAopEntity 的属性设置为查询条件
Object params = joinPoint.getArgs()[0];
DataScopeType dataScopeType = dataScope.dataScopeType();
// 转为权限类型,向上转型
DataScopeAopEntity dataScopeAop = (DataScopeAopEntity) params;
// 基于数据权限的类型,设置用户的权限值
dataScopeType.fillDataScopeType(dataScopeAop, loginUser.getDeptIds());
}
// 执行proceed方法的作用是让目标方法执行
return joinPoint.proceed();
} catch (Exception e) {
return new BaseException(e.getMessage());
}
}
}
三、数据范围枚举
package com.dashu.park.common.enums;
import com.dashu.park.common.bean.DataScopeAopEntity;
import lombok.Getter;
import java.util.List;
/**
* 数据权限的类型
*/
@Getter
public enum DataScopeType {
/**
* 基于用户自己的数据权限
*/
DATA_SCOPE_USER {
@Override
public void fillDataScopeType(DataScopeAopEntity dataScopeAop, List<Long> dataScopeIds) {
dataScopeAop.setUserId(dataScopeIds.get(0));
}
},
/**
* 基于岗位的数据权限
*/
DATA_SCOPE_POST {
@Override
public void fillDataScopeType(DataScopeAopEntity dataScopeAop, List<Long> dataScopeIds) {
dataScopeAop.setPostId(dataScopeIds);
}
},
/**
* 基于部门的数据权限
*/
DATA_SCOPE_DEPT {
@Override
public void fillDataScopeType(DataScopeAopEntity dataScopeAop, List<Long> dataScopeIds) {
dataScopeAop.setDeptIds(dataScopeIds);
}
},
/**
* 基于组织,用户组的数据权限
*/
DATA_SCOPE_ORGANIZATION {
@Override
public void fillDataScopeType(DataScopeAopEntity dataScopeAop, List<Long> dataScopeIds) {
dataScopeAop.setOrganizationIds(dataScopeIds);
}
};
/**
* 设置数据权限的校验类型
*
* @param dataScopeAop 范围类型
* @param dataScopeIds 范围类型的Id列表
*/
public abstract void fillDataScopeType(DataScopeAopEntity dataScopeAop, List<Long> dataScopeIds);
}
四、登录用户信息
package com.dashu.park.common.bean;
import com.dashu.park.api.bean.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* @author HaiMa
* @date 2021/6/30
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class LoginUser extends BaseEntity {
@ApiModelProperty(value = "项目ID")
private Long pId;
@ApiModelProperty(value = "用户名ID")
private Long userId;
@ApiModelProperty(value = "昵称")
private String name;
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "用户角色")
private List<UserRole> userRoles;
@ApiModelProperty(value = "登录时间")
private Long loginTime;
@ApiModelProperty(value = "场景 WEB(默认) APP")
private String scenes;
@ApiModelProperty(value = "绑定权限ID")
private List<Long> deptIds;
@Data
public static class UserRole {
@ApiModelProperty(value = "角色ID")
private Long roleId;
@ApiModelProperty(value = "角色名")
private String roleName;
@ApiModelProperty("场景 WEB(默认) APP")
private String scenes;
}
}
五、权限相关的工具类
package com.dashu.park.common.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.dashu.park.api.response.global.RC_C;
import com.dashu.park.common.bean.BaseDaoByEntity;
import com.dashu.park.common.bean.LoginUser;
import com.dashu.park.common.constants.CommonConstants;
import com.dashu.park.common.exception.BaseException;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
public class SecurityUtils {
/**
* 获取登录的用户信息
*/
public static LoginUser getLoginUser() {
HttpServletRequest request = ServletUtils.getRequest();
if (request == null) {
throw new BaseException("request 不存在");
}
try {
LoginUser loginUser = (LoginUser) request.getAttribute(CommonConstants.LOGIN_AUTH_USER);
if (loginUser != null) {
return loginUser;
}
String data = request.getHeader(CommonConstants.LOGIN_AUTH_USER);
if (StringUtils.isEmpty(data)) {
throw new BaseException(RC_C.NO_LOGIN);
}
data = EncodesUtils.decodeBase64ToStr(data);
loginUser = JSON.parseObject(data, LoginUser.class);
if (loginUser == null) {
throw new BaseException(RC_C.NO_LOGIN);
}
request.setAttribute(CommonConstants.LOGIN_AUTH_USER, loginUser);
return loginUser;
} catch (Exception e) {
throw new BaseException(RC_C.NO_LOGIN);
}
}
/**
* 获取登录的用户信息
*/
public static LoginUser getLoginUserIgnoreError() {
try {
return getLoginUser();
} catch (Exception e) {
return null;
}
}
/**
* 获取项目ID
*/
public static long getProjectId() {
HttpServletRequest request = ServletUtils.getRequest();
if (request == null) {
Long projectId = CommonUtils.getAsynThreadDataProjectId();
if (projectId != null) {
return projectId;
}
throw new BaseException(RC_C.PROJECT_ERROR);
}
try {
// return EnvUtils.isDev() ? 101 : Long.parseLong(request.getHeader(CommonConstants.PROJECT_ID));
return Long.parseLong(request.getHeader(CommonConstants.PROJECT_ID));
} catch (Exception e) {
throw new BaseException(RC_C.PROJECT_ERROR);
}
}
/**
* 设置登录信息
*
* @param t
* @param <T>
*/
public static <T extends BaseDaoByEntity> void fillLoginUser(T t) {
AssertUtil.isTrue(ObjectUtil.isNull(t), "填充登录用户信息的类不能为空");
LoginUser loginUser = getLoginUser();
if (ObjectUtil.isNull(t.getCreateId())) {
t.setCreateId(loginUser.getUserId());
t.setUpdateId(loginUser.getUserId());
t.setCreateName(loginUser.getUsername());
t.setUpdateName(loginUser.getUsername());
}
}
public static <T extends BaseDaoByEntity> void fillUpdateLoginUser(T t) {
AssertUtil.isTrue(ObjectUtil.isNull(t), "填充登录用户信息的类不能为空");
LoginUser loginUser = getLoginUser();
if (ObjectUtil.isNull(t.getUpdateId())) {
t.setUpdateId(loginUser.getUserId());
t.setUpdateName(loginUser.getUsername());
}
}
/**
* 是否是管理员
*/
public static boolean isAdmin(LoginUser loginUser) {
if (StrUtil.isBlank(loginUser.getUsername())) {
return false;
}
return "admin".equals(loginUser.getUsername());
}
/**
* 校验是否有角色
*
* @param loginUser
*/
public static void checkRole(LoginUser loginUser) {
AssertUtil.isTrue(CollUtil.isEmpty(loginUser.getUserRoles()), "用户没有分配角色");
}
/**
* 校验是否有权限
*
* @param loginUser
*/
public static void checkDataScope(LoginUser loginUser) {
AssertUtil.isTrue(CollUtil.isEmpty(loginUser.getDeptIds()), "用户没有分配权限");
}
}
六、AOP数据权限校验基础类
数据权限校验基础类
package com.dashu.park.common.bean;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List;
@Data
@ToString
@EqualsAndHashCode
public class DataScopeAopEntity {
/**
* 用户ID
*/
@ApiModelProperty(value = "用户ID",hidden = true)
private Long userId;
/**
* 岗位
*/
@ApiModelProperty(value = "岗位",hidden = true)
private List<Long> postId;
/**
* 部门Id
*/
@ApiModelProperty(value = "岗位",hidden = true)
private List<Long> deptIds;
/**
* 组织Id
*/
@ApiModelProperty(value = "组织Id",hidden = true)
private List<Long> organizationIds;
}
数据权限校验入参
package com.interfaces.dept.dto;
import com.dashu.park.common.bean.BasePageDTO;
import com.dashu.park.common.bean.DataScopeAopEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author DASHU
*/
@Data
public class DeptPageDto extends DataScopeAopEntity {
@ApiModelProperty("组织链Id")
private Long deptLinkId;
@ApiModelProperty("父级Id")
private Long parentId;
@ApiModelProperty("组织名称")
private String name;
@ApiModelProperty("表示最大获取级别")
private Integer maxLevel;
@ApiModelProperty("级别")
private Integer level;
@ApiModelProperty(
value = "页码,缺省值:1",
example = "1"
)
private int pageNum = 1;
@ApiModelProperty(
value = "页数,缺省值:10",
example = "10"
)
private int pageSize = 10;
}
七、使用AOP校验数据权限
/**
* 分页获取组织信息
* @param dto
* @return
*/
@ApiOperation(value = "分页获取组织信息")
@GetMapping("/page")
@DataScope(checkRole = true,checkDataScope = true,dataScopeType = DataScopeType.DATA_SCOPE_DEPT)
R<DeptListVo> page(DeptPageDto dto){
return deptService.page(dto);
}
参数解析
checkRole = true,//检查角色
checkDataScope = true, //检查数据
dataScopeType = DataScopeType.DATA_SCOPE_DEP //使用部门方式校验