共计 4053 个字符,预计需要花费 11 分钟才能阅读完成。
一、场景介绍
在开发过程中很多时候咱们须要依据某些条件去做数据权限,比方:A 组织只能看见 A 组织及其上司组织的数据,B 部门只能看见本人的数据、等等,此时如果每次都去本人写 SQL 进行校验就会显得代码十分臃肿,因为就产生了本人去定义一套全局专用的数据权限过滤形式。
二、实现思路
借助于 Spring 的拦截器或过滤器,当申请进入到 Controller 时,将该用户的数据权限信息存入数据权限上下文中,在 MyBatis 执行 SQL 之前将其动静拼接下来,MyBatis-Plus 给咱们提供了一个 DataPermissionHandler 接口用于做数据权限管制,其外围调用逻辑位于 DataPermissionInterceptor 中,因而咱们本人须要定义一个 DataPermissionHandler 实现类将其注入到 MybatisPlusInterceptor 中
三、实现步骤
-
定义数据权限配置对象
@Data public class DataAuthConfig { private Map<String, Object> params; private DataAuthConfig() {} /** * 构建实例 * * @return com.minportal.platform.common.mybatis.core.DataPermissionContextHolder * @author Guo Shuai * @date 2022/7/25 * @since 1.0 **/ public static DataAuthConfig create() {DataAuthConfig config = new DataAuthConfig(); config.setParams(new ConcurrentHashMap<>(4)); return config; } /** * 增加参数 * * @param key sql 字段名称 * @param value 值 * @return java.util.Map<java.lang.String, java.lang.Object> * @author Guo Shuai * @date 2022/7/25 * @since 1.0 **/ public DataAuthConfig withParam(String key, Object value) { // 设置参数 params.put(key, value); // 增加参数 return this; } }
-
定义数据权限上下文
public class DataAuthContextHolder {private static final ThreadLocal<DataAuthConfig> CONTEXT = new ThreadLocal<>(); /** * 获取配置 * * @return java.util.Map<java.lang.String, java.lang.Object> * @author Guo Shuai * @date 2022/7/25 * @since 1.0 **/ public static DataAuthConfig getContext() {if (Objects.isNull(CONTEXT.get())) {synchronized (DataAuthContextHolder.class) { // 判断是否为空 if (Objects.isNull(CONTEXT.get())) {CONTEXT.set(DataAuthConfig.create()); } } } // 返回 return CONTEXT.get();} /** * 清空数据 * * @author Guo Shuai * @date 2022/7/25 * @since 1.0 **/ public static void clean() {CONTEXT.remove(); } }
-
实现 DataPermissionHandler 接口
/** * 全局数据权限处理器 * * @author Guo Shuai * @version 1.0 * @date 2022/7/25 */ public class GlobalDataAuthHandler implements DataPermissionHandler { @Override public Expression getSqlSegment(Expression where, String mappedStatementId) { // 从数据权限上下文获取数据权限参数列表 DataAuthConfig config = DataAuthContextHolder.getContext(); // 判断是否为空 if (config == null || CollUtil.isEmpty(config.getParams())) {return where;} else {return dataScopeFilter(where, config.getParams()); } } /** * 构建过滤条件 * * @param where 条件对象 * @param conditions 条件列表 * @return net.sf.jsqlparser.expression.Expression * @author Guo Shuai * @date 2022/7/25 * @since 1.0 **/ public static Expression dataScopeFilter(Expression where, Map<String, Object> conditions) { // 定义条件 AtomicReference<Expression> whereAtomic = new AtomicReference<>(where); // 循环结构条件 conditions.forEach((key, value) -> { // 判断 value 的类型(汇合非凡解决)if (value instanceof Collection) {Collection<?> collection = (Collection<?>) value; InExpression expression = new InExpression(); expression.setLeftExpression(new Column(key)); // 获取条件 ItemsList itemsList = new ExpressionList(collection.stream().map(String::valueOf).map(StringValue::new).collect(Collectors.toList())); expression.setRightItemsList(itemsList); // 拼接条件 whereAtomic.set(new AndExpression(whereAtomic.get(), expression)); } else {whereAtomic.set(new AndExpression(whereAtomic.get(), new EqualsTo(new Column(key), new StringValue(String.valueOf(value))))); } }); return whereAtomic.get();} }
-
将实现的拦截器注入到 MybatisPlusInterceptor 中
@Configuration public class MybatisPlusConfig { /** * 分页插件和数据权限插件 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 数据权限 interceptor.addInnerInterceptor(new DataPermissionInterceptor(new GlobalDataAuthHandler())); return interceptor; } }
-
定义 Spring 拦截器解决数据权限上下文参数
@Component public class DataAuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 设置数据权限信息 // 例 A 部门只能看本人的数据及其子级部门 A1 的数据 DataAuthContextHolder.getContext().withParam("organId", Lists.newArrayList("A", "A1")); return HandlerInterceptor.super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {DataAuthContextHolder.clean(); } }
四、总结
对于不是特地简单的 SQL 结构来说应该能够实现大部分的数据权限过滤,这里数据类型只辨别了汇合和其余类型,若本人的业务有其余要求的话能够对其进行革新拓展。
正文完