需求

为了增强程序的安全性,需要在用户访问数据库的时候进行权限判断后选择性进行判断是否需要增强sql,来达到限制低级别权限用户访问数据的目的.

根据业务需要,这里将角色按照数据范围做权限限定.比如,角色如下:

编号

名称

描述

1

管理员

全部数据权限

2

普通角色

自定义数据权限

3

部门权限

部门权限

4

部门及以下数据权限

部门及以下数据权限

5

本人数据

本人数据

部门如下:

编号

父id

名称

描述

1

0

北京总公司

101

1

北京公司1

102

1

北京公司2

10101

101

丰台公司1

10102

101

丰台公司2

10201

102

昌平公司1

10202

102

昌平公司2

思路:

  1. 可以模仿 PageHelper.startPage 用来声明哪些操作需要做范围限制
  2. 定义 Mybatis plus自定义拦截器Interceptor,用于每次拦截查询sql语句,附带数据范围权限sql条件
  3. 因为使用了 PageHelper.startPage 分页插件的使用,先计算总数,怎么在这之前拦截,需要拦截多次
  4. 考虑到Mybatis拦截器能够拦截SQL执行的整个过程,因为我们可以考虑SQL执行之前,对SQL进行重写,从而达到数据行权限的目的。

步骤:

  1. 声明datescope

    protected static final ThreadLocal threadLocal = new ThreadLocal();
    /**

    • 设置权限标识
      /
      public static void startDataScope(){
      threadLocal.set(SecurityConstants.DATA_SCOPE);
      }
      /
      *
    • 获取权限标识
      /
      public static String getDataScope(){
      return threadLocal.get();
      }
      /
      *
    • 清除权限标识
      */
      public static void cleanDataScope(){
      threadLocal.remove();
      }
      复制代码
  2. 定义 Mybatis plus自定义拦截器

    /**

    • @author zhc

    • @description 数据权限插件

    • @date 2022-04-01 17:03
      */
      @Intercepts(
      {
      @Signature(type = Executor.class, method = “query”, args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
      @Signature(type = Executor.class, method = “query”, args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
      }
      )
      @Slf4j
      public class MybatisDataPermissionIntercept implements Interceptor {
      CCJSqlParserManager parserManager = new CCJSqlParserManager();

      /**

      • 全部数据权限
        */
        public static final String DATA_SCOPE_ALL = “1”;

      /**

      • 自定数据权限
        */
        public static final String DATA_SCOPE_CUSTOM = “2”;

      /**

      • 部门数据权限
        */
        public static final String DATA_SCOPE_DEPT = “3”;

      /**

      • 部门及以下数据权限
        */
        public static final String DATA_SCOPE_DEPT_AND_CHILD = “4”;

      /**

      • 仅本人数据权限
        */
        public static final String DATA_SCOPE_SELF = “5”;

      @Override
      public Object intercept(Invocation invocation) throws Throwable {

       try {Object[] args = invocation.getArgs();MappedStatement ms = (MappedStatement) args[0];Object parameter = args[1];RowBounds rowBounds = (RowBounds) args[2];ResultHandler resultHandler = (ResultHandler) args[3];Executor executor = (Executor) invocation.getTarget();CacheKey cacheKey;BoundSql boundSql;//由于逻辑关系,只会进入一次if (args.length == 4) {//4 个参数时boundSql = ms.getBoundSql(parameter);cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);} else {//6 个参数时cacheKey = (CacheKey) args[4];boundSql = (BoundSql) args[5];}//TODO 自己要进行的各种处理String sql = boundSql.getSql();log.info("原始SQL: {}", sql);//判断线程内是否有权限信息String dataScope = SecurityUtils.getDataScope();if (SecurityConstants.DATA_SCOPE.equals(dataScope)){// 增强sqlSelect select = (Select) parserManager.parse(new StringReader(sql));SelectBody selectBody = select.getSelectBody();if (selectBody instanceof PlainSelect) {this.setWhere((PlainSelect) selectBody);} else if (selectBody instanceof SetOperationList) {SetOperationList setOperationList = (SetOperationList) selectBody;List<SelectBody> selectBodyList = setOperationList.getSelects();selectBodyList.forEach((s) -> {this.setWhere((PlainSelect) s);});}String dataPermissionSql = select.toString();log.info("增强SQL: {}", dataPermissionSql);BoundSql dataPermissionBoundSql = new BoundSql(ms.getConfiguration(), dataPermissionSql, boundSql.getParameterMappings(), parameter);return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, dataPermissionBoundSql);}//注:下面的方法可以根据自己的逻辑调用多次,在分页插件中,count 和 page 各调用了一次return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);} finally {//清除线程中权限参数SecurityUtils.cleanDataScope();}
      

      }

      @Override
      public Object plugin(Object target) {
      return Plugin.wrap(target, this);
      }

      @Override
      public void setProperties(Properties properties) {
      }

      protected void setWhere(PlainSelect plainSelect) {
      Expression sqlSegment = this.getSqlSegment(plainSelect.getWhere());
      if (null != sqlSegment) {
      plainSelect.setWhere(sqlSegment);
      }
      }

      @SneakyThrows
      public Expression getSqlSegment(Expression where) {
      JSONObject loginUser = getLoginUser();
      if (loginUser == null){
      return where;
      }
      Integer deptId = loginUser.getInteger(“deptId”);
      String userId = loginUser.getString(“userId”);
      JSONArray roles = loginUser.getJSONArray(“roles”);
      StringBuilder sqlString = new StringBuilder();
      for (Object role : roles) {
      JSONObject roleJson = JSONObject.parseObject(role.toString());
      String dataScopeNum = roleJson.getString(SecurityConstants.DATA_SCOPE);
      Integer roleId = roleJson.getInteger(“roleId”);
      if (DATA_SCOPE_ALL.equals(dataScopeNum)) {
      // 全部数据权限
      sqlString = new StringBuilder();
      break;
      } else if (DATA_SCOPE_CUSTOM.equals(dataScopeNum)) {
      sqlString.append(" OR sys_dept.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = ‘“)
      .append(roleId)
      .append(”’ ) “);
      } else if (DATA_SCOPE_DEPT.equals(dataScopeNum)) {
      sqlString.append(” OR sys_dept.dept_id = ‘“).append(deptId).append(”’ “);
      } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScopeNum)) {
      sqlString.append(” OR sys_dept.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = ‘“)
      .append(deptId)
      .append(”’ or find_in_set( ‘“)
      .append(deptId)
      .append(”’ , ancestors ) ) “);
      }else if (DATA_SCOPE_SELF.equals(dataScopeNum)) {
      //TODO 暂时有问题
      sqlString.append(” OR sys_user.user_id = ‘“).append(userId).append(”’ “);
      }
      }
      if (StringUtils.isNotBlank(sqlString.toString())) {
      if (where == null){
      where = new HexValue(” 1 = 1 “);
      }
      sqlString.insert(0,” AND (“);
      sqlString.append(”)");
      sqlString.delete(7, 9);
      //判断是不是分页, 分页完成之后 清除权限标识
      return CCJSqlParserUtil.parseCondExpression(where + sqlString.toString());
      }else {
      return where;
      }
      }

    }
    复制代码

  3. 修改MybatisDataPermissionIntercept在PageHelper插件后面执行

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    com.zhc.cloud.mybatis.intercept.MybatisInterceptorAutoConfiguration

    /**

    • @author zhc

    • @description 数据权限插件 配置

    • @date 2022-04-01 17:01
      */
      @AutoConfigureAfter(PageHelperAutoConfiguration.class)
      @Configuration
      public class MybatisInterceptorAutoConfiguration implements InitializingBean{

      @Autowired
      private List sqlSessionFactoryList;

      @Override
      @PostConstruct
      public void afterPropertiesSet() throws Exception {
      //创建自定义mybatis拦截器,添加到chain的最后面
      MybatisDataPermissionIntercept mybatisInterceptor = new MybatisDataPermissionIntercept();

       for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();//自己添加configuration.addInterceptor(mybatisInterceptor);}
      

      }

    }

先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

使用mybatis plus自定义拦截器,实现数据权限相关推荐

  1. 基于mybatis拦截器实现数据权限

    数据权限是很多系统常见的功能,实现的方式也是很多的,最近在做项目的时候,自己基于mybatis拦截器做了一个数据权限的功能. **功能设计 a)  需要做数据权限功能的表加上一个权限id字段. 权限i ...

  2. 第1节 flume:15、flume案例二,通过自定义拦截器实现数据的脱敏

    1.7.flume案例二 案例需求: 在数据采集之后,通过flume的拦截器,实现不需要的数据过滤掉,并将指定的第一个字段进行加密,加密之后再往hdfs上面保存 原始数据与处理之后的数据对比 图一  ...

  3. springboot注解 + mybatisplus拦截器实现数据权限拦截(兼容分页插件)

    需求 要求在同一个数据请求方法中,根据不同的权限返回不同的数据集,而且无需并且不能由研发编码控制. 设计思路 竟然要实现查询语句与权限解耦,第一想法联想到的就是AOP,拦截所有的底层sql,加入过滤条 ...

  4. Struts2自定义拦截器实例—登陆权限验证

    版本:struts2.1.6 此实例实现功能:用户需要指定用户名登陆,登陆成功进入相应页面执行操作,否则返回到登陆页面进行登陆,当直接访问操作页面(登陆后才能访问的页面)时则不允许,须返回登陆页面. ...

  5. mybatis 自定义拦截器

    拦截器注解 mybatis自定义拦截器实现步骤: 实现org.apache.ibatis.plugin.Interceptor接口. 添加拦截器注解org.apache.ibatis.plugin.I ...

  6. Struts2自定义类型转换器、自定义拦截器和用户输入数据的验证

    一.自定义类型转换器 1.编写一个类,继承com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter 2.覆盖掉其中的public Obj ...

  7. WebServices中使用cxf开发日志拦截器以及自定义拦截器

    首先下载一个cxf实例,里面包含cxf的jar包.我下的是apache-cxf-2.5.9 1.为什么要设置拦截器? 为了在webservice请求过程中,能动态操作请求和响应数据, CXF设计了拦截 ...

  8. SpringMVC自定义拦截器与异常处理(自定义异常)

    SpringMVC自定义拦截器与异常处理 拦截器概念 拦截器代码演示 创建maven工程 pom.xml <?xml version="1.0" encoding=" ...

  9. Struts2 自定义拦截器(easy example)

    要自定义拦截器需要实现com.opensymphony.xwork2.interceptor.Interceptor接口: 新建一个MyIntercept package com.action;imp ...

最新文章

  1. 2016校招内推 -- 阿里巴巴前端 -- 四面面试经历
  2. zcmu 4935(排序)
  3. 玩吧高速增长的数据上云实践
  4. RGB 24和YUY2相互转换
  5. LeetCode二分法---C语言
  6. 数人云CTO解读Docker 1.12和金融业容器化
  7. 写一个function,清除字符串前后的空格。(兼容所有浏览器)
  8. 洛谷——P1001 A+B Problem
  9. ASP.Net 中常用的数据库连接方式
  10. ssh无密码登录设置方法以及出现问题 ECDSA host key 和IP地址对应的key不同的解决...
  11. hane WIN nfs配置
  12. 一款快速搭建局域网http服务器的神器
  13. 谷歌、斯坦福联合发文:我们为什么一定要用大模型?
  14. 人家这才叫软件测试工程师,你那只是混口饭吃(附HR面试宝典)
  15. linux之文件压缩解压
  16. 人世间最纯净的友情只存在于孩童时代
  17. jquery param 数组 带有 %5B%5D [] 问题
  18. JAVA EE项目开发及应用实训报告——网上考试系统
  19. 基于电影爬虫及Spark数据分析可视化设计
  20. amoled和super amoled哪个更伤眼 amoled和super amoled的显示效果

热门文章

  1. 2021-2025年中国柴油废气油液(DEF)加热器行业市场供需与战略研究报告
  2. Maven Nexus搭建本地私服 上传jar包或本地项目到私服
  3. 生、死、腾讯、360
  4. 名悦集团:什么是汽车强制报废?有什么标准?
  5. conformer勘误(自用)
  6. ubuntu14.04中利用samba访问linux/windows共享目录的方法
  7. oracle游标的使用例子,oracle游标的例子
  8. 清理c盘空间的方法总结,最详细的c盘清理攻略
  9. python——炸金花
  10. 【日语】标日初级上册单词(17-20)1