- 解决的问题
在项目开发中系统定义两种用户管理用户和企业用户,管理用户可以查看全部的数据,而企业用户只能看自己企业的数据,但是路由权限是一样的。就需要在数据库查询的时候添加相应的条件。>在项目开发中系统定义两种用户管理用户和企业用户,管理用户可以查看全部的数据,而企业用户只能看自己企业的数据,但是路由权限是一样的。就需要在数据库查询的时候添加相应的条件。
- 思路
使用spring的拦截器,在mybatis执行SQL之前判断是否有权限和是否需要添加权限,没有就自动拼接条件。MyBatis-Plus给我们提供了一个DataPermissionHandler接口用于做数据权限控制,其核心调用逻辑位于DataPermissionInterceptor中,因此我们自己需要定义一个DataPermissionHandler实现类将其注入到MybatisPlusInterceptor中
定义是否需要验证权限的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE,ElementType.Method)
@Documented
public @interface DataAuth(){
boolean isAuth() default true;
}
定义数据权限配置对象
@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) {
if(null == where ) return;
//从数据权限上下文获取数据权限参数列表
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()));
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
定义Spring拦截器处理数据权限上下文参数
@Component
public class DataAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取用户登录信息
LoginUser user = UserUtil.getLoginUser();
// 获取是否有注解
DataAuth auth = null;
if(handle instanceof HandlerMethod){
HandlerMethod method = (HandlerMethod) handler;
DataAuth anno = method.getMethod().getDeclaringClass().getAnnotation(DataAuth.class);
auth = anno == null ? method.getMethodAnnotation(DataAuth.class) : anno;
}
// 获取用户的角色或者是否有对应的权限,如果没有,则需要添加相应的限制
...
if(没有权限 and auth.isAuth()){
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();
}
}
拦截器不生效解决
@Configuration
public class ResourcesConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new GlobalDataAuthHandler()).addPathPatterns("/**");
}
}
- 使用
在对应controller的方法或者类上添加 @DataAuth 注解