余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

Mybatis-plus 添加拦截器,实现简单数据权限

xiyangw 2023-09-16 15:03 9 浏览 0 评论


系统需要根据用户所属的公司,来做一下数据权限控制。
具体一点,就是通过表中的
company_id 进行权限控制。
项目使用的是 mybatis-plus ,所以通过添加拦截器的方式,修改查询 sql,实现数据权限。

1 配置文件中的配置

yaml复制代码# 数据权限配置  
data-permission:  
  # 不再数据权限的表,目前主要是公司信息表,和一些关联表  
  not-control-tables: role_menu,user_company,user_role  
  # 权限控制表,即基于哪个表的数据来做权限区分,目前是公司信息表  
  base-table: company_info  
  # 特殊的uri,不进行数据权限控制  
  not-control-uri: /checkCompany-post

2 在权限处理时,将请求 uri 放入到内存中

java复制代码/**  
 * 自定义权限处理  
 */  
@Component  
@Slf4j  
public class CustomAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {  
 
    @Override  
    public AuthorizationDecision check(  
            Supplier<Authentication> authentication,  
            RequestAuthorizationContext requestAuthorizationContext  
    ) {  
  
	   // …… 
  
        HttpServletRequest request = requestAuthorizationContext.getRequest();  
        String method = request.getMethod();  
        String path = request.getRequestURI();  
  
        // 将当前的请求的信息,放入到user中,用户后面的数据权限  
        LoginUser loginUser = (LoginUser) authentication.get().getPrincipal();  
        loginUser.setUri(path + "-" + method.toLowerCase());  
	  // ……
    }
    
}

关于 AuthorizationManager 请参见: www.51cto.com/article/700…

另外,用户在的登录系统之后,有一个选择公司的动作,这时将用户选择的公司信息放入缓存中:

java复制代码// ……
// 缓存用户选择的公司  
RBucket<String> bucket = redissonClient.getBucket(OPERATION_COMPANY + loginUserId);  
bucket.set(companyId, Duration.ofHours(2));
// ……

3 拦截器中的配置

java复制代码import cn.hutool.core.util.ObjectUtil;  
import cn.hutool.core.util.StrUtil;  
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;  
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;  
import lombok.Data;  
import lombok.extern.slf4j.Slf4j;  
import net.sf.jsqlparser.JSQLParserException;  
import net.sf.jsqlparser.expression.Expression;  
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;  
import net.sf.jsqlparser.parser.CCJSqlParserUtil;  
import net.sf.jsqlparser.statement.select.PlainSelect;  
import net.sf.jsqlparser.statement.select.Select;  
import org.apache.ibatis.executor.Executor;  
import org.apache.ibatis.mapping.BoundSql;  
import org.apache.ibatis.mapping.MappedStatement;  
import org.apache.ibatis.session.ResultHandler;  
import org.apache.ibatis.session.RowBounds;  
import org.redisson.api.RBucket;  
import org.redisson.api.RedissonClient;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.security.core.Authentication;  
import org.springframework.security.core.context.SecurityContextHolder;  
import org.springframework.stereotype.Component;  
  
import java.sql.SQLException;  
import java.util.Arrays;  
import java.util.List;  
  

  
/**  
 * 数据权限控制  
 */  
@Data  
@Component  
@Slf4j  
public class DataPermissionInterceptor implements InnerInterceptor {  
  
    @Autowired  
    private RedissonClient redissonClient;  
  
    @Value("${data-permission.not-control-tables}")  
    public String notControlTables;  
  
    @Value("${data-permission.base-table}")  
    public String baseTable;  
  
    @Value("${data-permission.not-control-uri}")  
    public String notControlUri;  
  
    @Override  
    public boolean willDoQuery(  
            Executor executor,  
            MappedStatement ms,  
            Object parameter,  
            RowBounds rowBounds,  
            ResultHandler resultHandler,  
            BoundSql boundSql  
    ) throws SQLException {  
        return InnerInterceptor.super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);  
    }  
  
    @Override  
    public void beforeQuery(  
            Executor executor,  
            MappedStatement ms,  
            Object parameter,  
            RowBounds rowBounds,  
            ResultHandler resultHandler,  
            BoundSql boundSql  
    ) throws SQLException {  
  
        log.debug("数据权限处理……");  
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();  
        if (ObjectUtil.isNull(authentication)) {  
            log.debug("数据权限处理, 未登录!");  
            return;  
        }  
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();  
        LoginUser loginUser = (LoginUser) principal;  
        String username = loginUser.getUsername();  
        // 如果是系统管理员,不需做作权限处理  
        if (SYSTEM_ADMINISTRATOR_ACCOUNT.equals(username)) {  
            log.debug("数据权限处理,当前为管理员,不需要处理数据权限。");  
            return;  
        }  
        String uri = loginUser.getUri();  
        log.debug("数据权限处理,当前uri为:{}", uri);  
        if (notControlUri.contains(uri)) {  
            log.debug("数据权限处理,当前uri,不需要处理数据权限。");  
            return;  
        }  
  
  
        String sql = boundSql.getSql();  
        Select select;  
        try {  
            select = (Select) CCJSqlParserUtil.parse(sql);  
        } catch (JSQLParserException e) {  
            throw new RuntimeException(e);  
        }  
        // 系统自动生成的sql,一般都是单表查询,所以这里暂时不考虑复杂的情况  
        PlainSelect plainSelect = (PlainSelect) select.getSelectBody();  
        net.sf.jsqlparser.schema.Table table = (net.sf.jsqlparser.schema.Table) plainSelect.getFromItem();  
        String tableName = table.getName();  
        // 排除一些不需要控制的表  
        List<String> notControlTablesList = Arrays.asList(notControlTables.split(","));  
        if (notControlTablesList.contains(tableName.toLowerCase())) {  
            log.debug("数据权限处理,当前表不做权限控制,table is {}", tableName);  
            return;  
        }  
  
        String userId = loginUser.getUser().getPkId();  
        RBucket<String> bucket = redissonClient.getBucket(OPERATION_COMPANY + userId);  
        String companyId = bucket.get();  
        if (StrUtil.isBlank(companyId)) {  
            throw new BaseException("公司id不存在!");  
        }  
  
        // 处理SQL语句  
        // 基础表,根据主键进行控制  
        log.debug("数据权限处理,处理之前的sql为: {}", sql);  
        Expression where = plainSelect.getWhere();  
        Expression envCondition;  
        try {  
            if (baseTable.equals(tableName.toLowerCase())) {  
                envCondition = CCJSqlParserUtil.parseCondExpression("PK_ID = " + companyId);  
            } else {  
                envCondition = CCJSqlParserUtil.parseCondExpression("COMPANY_ID = " + companyId);  
            }  
        } catch (JSQLParserException e) {  
            throw new RuntimeException(e);  
        }  
        if (where == null) {  
            plainSelect.setWhere(envCondition);  
        } else {  
            AndExpression andExpression = new AndExpression(where, envCondition);  
            plainSelect.setWhere(andExpression);  
        }  
        sql = plainSelect.toString();  
        log.debug("数据权限处理,处理之后的sql为: {}", sql);  
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);  
        mpBs.sql(sql);  
    }  
}

关于 JSqlParser ,参见: juejin.cn/post/707219…

4 启用插件

java复制代码  
@Configuration  
@MapperScan("xxx.xxx.xx.mapper")  
public class MybatisPlusConfig {  
  
    @Autowired  
    private DataPermissionInterceptor dataPermissionInterceptor;  
  
    /**  
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)  
     */    @Bean  
    public MybatisPlusInterceptor mybatisPlusInterceptor() {  
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();  
        interceptor.addInnerInterceptor(dataPermissionInterceptor);  
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));  
        return interceptor;  
    }  
}

相关推荐

域名交易策略之如何在竞争激烈的拍卖市场中胜出

域名交易策略之如何在竞争激烈的拍卖市场中胜出作为在域名领域拥有丰富经验的专业人士,您或许早就注意到了域名拍卖。我们的网站提供实时的域名拍卖清单,如果您想更加深入的了解域名拍卖,看这篇文章准没错!Dyn...

icp备案和域名备案两者之间有什么区别?
icp备案和域名备案两者之间有什么区别?

目前备案域名是比较常见的产品,但是也有很多人不懂什么是备案域名,还有大家所说的ICP备案,它和域名备案有什么区别呢?下面和大家分享一下,希望对大家有所帮助。什...

2023-09-28 15:33 xiyangw

直接挑战支付宝、微信?抖音支付正式上线:首批支持十家银行
直接挑战支付宝、微信?抖音支付正式上线:首批支持十家银行

了解更多热门资讯、玩机技巧、数码评测、科普深扒,点击右上角关注我们----------------------------------1月19日,不少网友发现,...

2023-09-28 15:33 xiyangw

Firefox测试新版站点隔离功能 可将每个网站置于单独的进程中
Firefox测试新版站点隔离功能 可将每个网站置于单独的进程中

Mozilla当前正在Firefox每夜构建版(NightlyBuild)和Beta通道测试一项全新的安全体系架构,特点是能够将每个站点都置于单独的...

2023-09-28 15:31 xiyangw

如何给域名估值?

我们从域名注册商中购买了一个流行的域名,有时候申请注册了自己也不会去用,其实可以转卖给别人,但是价格比我们当前购买的时候会有很大的变化。打算开展新的业务,想要寻求一个新的域名,但是很多时候也不知道具体...

二进制部署k8s集群

环境准备kube-apiserver:使用节点本地nginx实现高可用;关闭非安全端口8080和匿名访问;在安全端口6443接收https请求;严格的认证和授权策略(x509、tok...

域名实名制认证
域名实名制认证

为贯彻国家工信部对域名持有者实名制审核管理的相关规范,现在已经全面推行域名实名认证。域名实名认证包括域名命名审核(指域名将由国家监管部门认定是否含有政策不允许注...

2023-09-28 15:30 xiyangw

dns巡检脚本

#!/bin/bash#定义需要巡检的DNS服务器地址DNS_SERVERS=("8.8.8.8""114.114.114.114")#定义需要解析的域...

字节跳动再次蓄力小程序,相关域名花落谁家?
字节跳动再次蓄力小程序,相关域名花落谁家?

2017年1月9日,腾讯的微信小程序上线;2018年9月,阿里巴巴的支付宝小程序上线;2019年,字节跳动也加入了小程序的队伍,然而......字节跳动又上线新...

2023-09-28 15:30 xiyangw

保护您的域名:迈出第一步
保护您的域名:迈出第一步

AkamaiDNS团队互联网上的每一个人和每一件事都依赖于域名系统(DNS)的正常运作。近年来,DNS一直是网络攻击的常见对象,2019年当然也不例外。大多数...

2023-09-28 15:30 xiyangw

利用阿里云API实现DDNS,不用花钱再用花生壳花生棒了
利用阿里云API实现DDNS,不用花钱再用花生壳花生棒了

前言:自从家里有小孩后手机里的照片多的没地方放,又想实时能查看,家里人的手机都拍了宝宝照片视频,也没法相互查看,所以这个场景下不管是云备份,qq空间都无法实现这...

2023-09-28 15:29 xiyangw

TOP域名突破210万,保留域名开始抢滩

2016年5月5日12:00,.top精品保留域名开放溢价注册。本次开放.top域名包括三字母、精品双拼域名,广大用户可根据自身需要选择中意域名。本次.top溢价域名详细列表,已上传公告附件。请广...

关于西部数码.CC/.TV域名开启实名认证通知
关于西部数码.CC/.TV域名开启实名认证通知

根据工信部网站发布信息,工业和信息化部同意威瑞信互联网技术服务(北京)有限公司成为“.CC”“.TV”(含中文.CC和中文.TV)顶级域域名注册管理机构。西部数...

2023-09-28 15:29 xiyangw

SEO也可以走捷径:域名的选择
SEO也可以走捷径:域名的选择

做SEO域名选择很关键,在选择网站域名的时候我们会考虑到很多的因素。如:域名中要包含英文关键词或拼音的形式来增加网站的信用度、域名中添加地区或地区缩写来圈定服务...

2023-09-28 15:29 xiyangw

副业-小白网站建设-Day02
副业-小白网站建设-Day02

非洲待的时间较长了,一直在考虑做副业,所以我的网站是为后续当地副业做准备,下面写写域名注册的步骤和坑。首先,建立一个自己的网站,第一步需要注册域名。域名注册需要...

2023-09-28 15:28 xiyangw

取消回复欢迎 发表评论: