一、场景介绍

在开发过程中很多时候咱们须要依据某些条件去做数据权限,比方:A组织只能看见A组织及其上司组织的数据,B部门只能看见本人的数据、等等,此时如果每次都去本人写SQL进行校验就会显得代码十分臃肿,因为就产生了本人去定义一套全局专用的数据权限过滤形式。

二、实现思路

借助于Spring的拦截器或过滤器,当申请进入到Controller时,将该用户的数据权限信息存入数据权限上下文中,在MyBatis执行SQL之前将其动静拼接下来,MyBatis-Plus给咱们提供了一个DataPermissionHandler接口用于做数据权限管制,其外围调用逻辑位于DataPermissionInterceptor中,因而咱们本人须要定义一个DataPermissionHandler实现类将其注入到MybatisPlusInterceptor中

三、实现步骤

  1. 定义数据权限配置对象

    @Datapublic 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; }}
  2. 定义数据权限上下文

    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(); }}
  3. 实现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(); }}
  4. 将实现的拦截器注入到MybatisPlusInterceptor中

    @Configurationpublic class MybatisPlusConfig { /**  * 分页插件和数据权限插件  */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() {     MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();     //数据权限     interceptor.addInnerInterceptor(new DataPermissionInterceptor(new GlobalDataAuthHandler()));     return interceptor; }}
  5. 定义Spring拦截器解决数据权限上下文参数

    @Componentpublic 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结构来说应该能够实现大部分的数据权限过滤,这里数据类型只辨别了汇合和其余类型,若本人的业务有其余要求的话能够对其进行革新拓展。