赞
踩
目录
由于业务场景需要,针对部分业务功能数据需要根据角色实现数据权限控制,数据关系比较复杂,有多表关联查询的场景,查阅一番资料,使用Mybatis拦截器--Interceptor,通过实现Interceptor接口,结合自定义注解,实现数据权限控制。哪里有不对的请各位大佬指出。
有用的话点个赞再走呗!
MyBatis 允许使用插件来拦截的方法调用包括:
具体原理可以参考:MyBatis拦截器原理介绍-简书
拦截器注解使用:
@Intercepts和@Signature注解。
@Intercepts
@Intercepts注解只有一个属性,即value,其返回值类型是一个@Signature类型的数组,表示我们可以配置多个@Signature注解。
@Signature
@Signature注解其实就是一个方法签名,其共有三个属性,分别为:
type指接口的class,
method指接口中的方法名,
args指的是方法参数类型(该属性返回值是一个数组)。
开发工具:IDEA
JDK: 1.8
springboot:2.1.8-RELEASE
框架:springboot+mybatisplus
数据库:mysql 5.7
redis: 7.0
target表示目标功能,如模板定义:iotapp,分区管理:devicearea
tid:表示目标功能表的主键id,用英文逗号隔开。
- CREATE TABLE `hd_user_permission` (
- `id` varchar(64) NOT NULL,
- `role_id` varchar(64) DEFAULT NULL COMMENT '角色id',
- `target` varchar(50) DEFAULT NULL COMMENT '目标模块',
- `tid` longtext COMMENT '授权对象id',
- `enable` int(1) DEFAULT NULL COMMENT '是否可用',
- `remark` varchar(500) DEFAULT NULL COMMENT '备注',
- `created` datetime DEFAULT NULL COMMENT '创建时间',
- `creator` varchar(20) DEFAULT NULL COMMENT '创建人',
- `updated` datetime DEFAULT NULL COMMENT '更新时间',
- `updator` varchar(20) DEFAULT NULL COMMENT '更新人',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户权限';
- package com.hdkj.hdiot.configure.entity;
-
- import com.baomidou.mybatisplus.annotation.TableName;
- import com.hdkj.hdiot.configure.entity.RootEntity;
- import lombok.Data;
- import lombok.EqualsAndHashCode;
-
- import java.util.Date;
-
- /**
- * 用户权限
- *
- * @author liuch liuchenghui@hdkjrd.com
- * @since 1.0.0 2022-06-22
- */
- @Data
- @EqualsAndHashCode(callSuper=false)
- @TableName("hd_user_permission")
- public class HdUserPermissionEntity extends RootEntity {
- private static final long serialVersionUID = 1L;
-
- /**
- * 角色id
- */
- private String roleId;
- /**
- * 目标模块
- */
- private String target;
- /**
- * 目标数据id
- */
- private String tid;
- /**
- * 增 删 改 查、可授予(级联) C R U D A
- */
- private String actions;
- /**
- * 是否可用
- */
- private Integer enable;
- /**
- * 备注
- */
- private String remark;
- }
基类:
- package com.hdkj.hdiot.configure.entity;
-
- import com.baomidou.mybatisplus.annotation.FieldFill;
- import com.baomidou.mybatisplus.annotation.TableField;
- import com.fasterxml.jackson.annotation.JsonFormat;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- import java.io.Serializable;
- import java.util.Date;
-
- /**
- * @author liuch
- * @title: RootEntity
- * @description: 基础实体类,物联网2.0用
- * @date 2022/2/18 13:40
- */
- @Data
- public abstract class RootEntity implements Serializable {
-
- @ApiModelProperty(value = "id")
- private String id;
-
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" , timezone = "GMT+8")
- @TableField(fill = FieldFill.INSERT)
- @ApiModelProperty(value = "创建时间")
- private Date created;
-
- @TableField(fill = FieldFill.INSERT)
- @ApiModelProperty(value = "创建人")
- private String creator;
-
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" , timezone = "GMT+8")
- @TableField(fill = FieldFill.INSERT_UPDATE)
- @ApiModelProperty(value = "更新时间")
- private Date updated;
-
- @TableField(fill = FieldFill.INSERT_UPDATE)
- @ApiModelProperty(value = "更新人")
- private String updator;
- }
crud接口省略;
-
-
- /**
- * @Author: 刘成辉
- * @Date: 2022/6/18 10:05
- * @Description:
- */
- @Component
- @Slf4j
- //@Order(99)
- public class HdInitCacheApplication implements ApplicationRunner {
-
- @Autowired
- HdUserPermissionService userPermissionService;
-
- @Override
- public void run(ApplicationArguments args) throws Exception {
- log.info("----------缓存用户权限-----------------");
- userPermissionService.initCache();
- log.info("--------缓存用户权限结束--------------");
- }
- }
角色id+目标功能模块作为key ,对象作为value,以Hash存储。
- @Override
- public void initCache() {
- HashOperations operations = redisTemplate.opsForHash();
- List<HdUserPermissionEntity> permissionEntityList = selectList(new QueryWrapper<>());
-
- for (HdUserPermissionEntity p : permissionEntityList) {
- operations.put(CacheConstant.PERMISSION,p.getRoleId()+"-"+p.getTarget(),p);
- }
-
- }
@UserPermission
-
- /**
- * @Author: 刘成辉
- * @Date: 2022/6/22 9:43
- * @Description:
- */
- @Documented
- @Target({ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface UserPermission {
-
- /**
- * 目标模块
- * @return
- */
- PermissionObject[] value();
- }
@PermissionObject
-
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target({})
- public @interface PermissionObject {
-
- /**
- * 授权目标
- */
- String target();
-
- /**
- * 表名
- */
- String tableName();
-
- /**
- * 别名
- */
- String tableAlias();
-
-
-
- }
注解是为了标记那个dao的方法需要实现数据权限,如:
- @UserPermission({
- @PermissionObject(target = "devicearea", tableName = "hd_device_area", tableAlias = "b"),
- @PermissionObject(target = "app", tableName = "hd_iot_app", tableAlias = "c"),
- @PermissionObject(target = "model", tableName = "hd_iot_model", tableAlias = "d"),
- })
- List<HDDevicePageRespDTO> selectPageList(@Param("params") Map<String, Object> params, IPage<HdDeviceEntity> page);
-
- @Slf4j
- @Intercepts(
- {
- @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
- }
- )
- public class UserPermissionHandler implements Interceptor {
-
- //@Autowired
- //RedisTemplate redisTemplate;
-
- /**
- * 是否开启用户权限验证
- */
- @Value("${hdiot.auth.enableDataPermission:#{false}}")
- boolean enableDataPermission;
-
-
- /**
- * 过滤权限接口uri白名单
- */
- @Value("${hdiot.auth.permissionWhiteList:#{null}}")
- private List<String> whitelist;
-
- @Override
- public Object intercept(Invocation invocation) throws Throwable {
- if (!enableDataPermission || inWhiteList()) {
- return invocation.proceed();
- }
- StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
- MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
-
- //获取拦截下的mapper
- MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
- if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
- return invocation.proceed();
- }
- UserPermission dataAuth = getDataAuth(mappedStatement);
-
- //没有注解直接放行,可根据情况补充更多的业务逻辑
- if (dataAuth == null) {
- return invocation.proceed();
- }
- //是否为mybatisPlus的count分页查询,以_mpCount结尾
- boolean innerCount = mappedStatement.getId().endsWith("_mpCount");
- //获取上一个拦截器传过来的sql
- BoundSql boundSql = statementHandler.getBoundSql();
- String orgSql = boundSql.getSql(); //获取到当前需要被执行的SQL
- //根据指定权限点对原有sql进行修改
- String sql = modifyOrgSql(orgSql, dataAuth, innerCount);
- metaObject.setValue("delegate.boundSql.sql", sql);
- return invocation.proceed();
- }
-
- @Override
- public Object plugin(Object o) {
- if (o instanceof StatementHandler) {
- return Plugin.wrap(o, this);
- }
- return o;
- }
-
- @Override
- public void setProperties(Properties properties) {
-
- }
-
- /**
- * 通过反射获取mapper方法是否加了数据拦截注解
- */
- private UserPermission getDataAuth(MappedStatement mappedStatement) throws ClassNotFoundException {
- UserPermission permission = null;
- String id = mappedStatement.getId();
- String className = id.substring(0, id.lastIndexOf("."));
- String methodName = id.substring(id.lastIndexOf(".") + 1);
- final Class<?> cls = Class.forName(className);
- final Method[] methods = cls.getMethods();
- for (Method method : methods) {
- if (method.getName().equals(methodName) && method.isAnnotationPresent(UserPermission.class)) {
- permission = method.getAnnotation(UserPermission.class);
- break;
- }
- }
- return permission;
- }
-
- /**
- * 根据权限点拼装对应sql
- *
- * @return 拼装后的sql
- */
- private String modifyOrgSql(String orgSQql, UserPermission userPermission, boolean innerCount) throws JSQLParserException {
- CCJSqlParserManager parserManager = new CCJSqlParserManager();
- Select select = (Select) parserManager.parse(new StringReader(orgSQql));
- PlainSelect plain = (PlainSelect) select.getSelectBody();
- List<Join> joins = plain.getJoins();
- if (innerCount) {
- SubSelect subSelect = (SubSelect) plain.getFromItem();
- plain = (PlainSelect) subSelect.getSelectBody();
- joins = plain.getJoins();
- }
- if (null == joins || joins.isEmpty()) {
- joins = new ArrayList<>();
- }
- String addWhere = "";
- List<String> whereList = new ArrayList<>();
- PermissionObject[] target = userPermission.value();
- for (PermissionObject permissionObject : target) {
- String where = getAllowDatas(permissionObject, joins);
- //String where = permissionObject.tableAlias() + ".id in(" + ids + ") ";
- whereList.add(where);
- }
- addWhere = String.join("and ", whereList);
-
- if (plain.getWhere() == null) {
- plain.setWhere(CCJSqlParserUtil.parseCondExpression(addWhere));
- } else {
- plain.setWhere(new AndExpression(plain.getWhere(), CCJSqlParserUtil.parseCondExpression(addWhere)));
- }
- plain.setJoins(joins);
- return select.toString();
- }
-
-
- /**
- * 获取有权限的数据
- *
- * @param target
- * @return
- */
- public String getAllowDatas(PermissionObject target, List<Join> joins) {
- String where = "%s.role_id in (%s) and %s.target = '%s'";
- Join join = new Join();
- //权限表别名
- String alais = "pre_" + target.target();
- List<String> partItems = new ArrayList<>();
- partItems.add("hd_user_permission");
- Table table = new Table(partItems);
- table.setAlias(new Alias(alais));
- join.setRightItem(table);
- EqualsTo equals = new EqualsTo();
- equals.setLeftExpression(new Column(alais + ".tid"));
- equals.setRightExpression(new Column(target.tableAlias() + ".id"));
- join.setOnExpression(equals);
- joins.add(join);
- //HashOperations operations = redisTemplate.opsForHash();
- List<String> dataIds = new ArrayList<>();
- //获取用户信息
- UserInfo user = BaseCtrl.getCurrentUser();
- log.info("权限用户:" + JSON.toJSONString(user));
-
- String ids = "";
- if (null == user || StringUtils.isBlank(user.getUserId())) {
- ids = "''";
- } else {
-
- }
- List<String> roleIds = user.getRoles();
- for (String dataId : roleIds) {
- if (roleIds.indexOf(dataId) == roleIds.size() - 1) {
- ids += ("'" + dataId + "'");
- break;
- }
- ids += ("'" + dataId + "',");
- }
- return String.format(where, alais, ids, alais, target.target());
- }
-
- private boolean inWhiteList() {
- ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
- if (null == attributes) {
- return false;
- }
- String requestURI = attributes.getRequest().getRequestURI();
- for (String s : whitelist) {
- if (requestURI.contains(s)) {
- return true;
- }
- }
- return false;
- }
- }
搞定,以上就是基于mybatis的数据权限控制了,使用的是join查询,sql语句记得加group by,避免出现重复数据,另外,记得给字段加索引!!!
有用的话点个赞再走呗!
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_24473507/article/details/126545343
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。