Sfoglia il codice sorgente

多租户全局处理器

dongxin 1 anno fa
parent
commit
d136de1c2e

+ 16 - 0
commons/src/main/java/com/jzg/annotation/IgnoreTenant.java

@@ -0,0 +1,16 @@
+package com.jzg.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 忽略多租户校验
+ *
+ * @author dongxin
+ */
+@Target({ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IgnoreTenant {
+}

+ 27 - 0
commons/src/main/java/com/jzg/aop/aspect/TenantAspect.java

@@ -0,0 +1,27 @@
+package com.jzg.aop.aspect;
+
+import com.jzg.core.base.TenantContext;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.stereotype.Component;
+
+/**
+ * 自定义注解切面类
+ *
+ * @author dongxin
+ */
+@Aspect
+@Component
+public class TenantAspect {
+
+    @Around("@annotation(com.jzg.annotation.IgnoreTenant)")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        try {
+            TenantContext.setInternalInvocation(true);
+            return joinPoint.proceed();
+        } finally {
+            TenantContext.clear();
+        }
+    }
+}

+ 87 - 0
commons/src/main/java/com/jzg/config/MultiTenantHandler.java

@@ -0,0 +1,87 @@
+package com.jzg.config;
+
+import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
+import com.jzg.core.base.BaseController;
+import com.jzg.core.base.TenantContext;
+import com.jzg.entity.properties.TenantProperties;
+import jodd.util.StringUtil;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
+
+import java.util.List;
+
+
+/**
+ * 多租户处理器实现类
+ *
+ * @author dongxin
+ */
+public class MultiTenantHandler implements TenantLineHandler {
+
+    private final TenantProperties properties;
+
+    private final BaseController controller;
+
+    public MultiTenantHandler(TenantProperties properties, BaseController controller) {
+        this.properties = properties;
+        this.controller = controller;
+    }
+
+    /**
+     * 获取租户ID值表达式,只支持单个ID值 (实际应该从用户信息中获取)
+     *
+     * @return 租户ID值表达式
+     */
+    @Override
+    public Expression getTenantId() {
+        // 实际应该从用户信息中获取,框架一般都有获取用户信息的接口
+        String userName = controller.getUserName();
+        String tenant_id = "111";
+        // 判空校验
+        if (StringUtil.isNotEmpty(tenant_id)) {
+            return new StringValue(tenant_id);
+        }
+        return new StringValue("");
+    }
+
+    /**
+     * 获取租户字段名,默认字段名叫: tenant_id
+     *
+     * @return 租户字段名
+     */
+    @Override
+    public String getTenantIdColumn() {
+        return properties.getColumn();
+    }
+
+    /**
+     * 根据表名判断是否忽略拼接多租户条件
+     * <p>
+     * 默认都要进行解析并拼接多租户条件
+     *
+     * @param tableName 表名
+     * @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件
+     */
+    @Override
+    public boolean ignoreTable(String tableName) {
+        // 自定义注解忽略拦截(可选,有的场景是获取不到登录用户信息的,比如系统内部调用接口,所以用一个内存变量来存储是否忽略校验)
+        if (TenantContext.isInternalInvocation()) {
+            return true;
+        }
+
+        // 可以自行增加一些校验比如获取不到用户就直接 return false 之类的。
+        List<String> ignoreLoginNames = properties.getIgnoreLoginNames();
+        // 忽略指定用户对租户的数据过滤
+        String username = "";
+        if (null != ignoreLoginNames && ignoreLoginNames.contains(username)) {
+            return true;
+        }
+
+        // 忽略指定表对租户数据的过滤
+        List<String> ignoreTables = properties.getIgnoreTables();
+        if (null != ignoreTables && ignoreTables.contains(tableName)) {
+            return true;
+        }
+        return false;
+    }
+}

+ 15 - 1
commons/src/main/java/com/jzg/config/MyBatisPlusConfig.java

@@ -3,17 +3,27 @@ package com.jzg.config;
 
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
+import com.jzg.core.base.BaseController;
+import com.jzg.entity.properties.TenantProperties;
 import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
 
+@EnableTransactionManagement(proxyTargetClass = true)
 @Configuration
+@EnableConfigurationProperties(TenantProperties.class)
 public class MyBatisPlusConfig {
 
     @Autowired
     RedissonClient redissonClient;
 
+    @Autowired
+    BaseController baseController;
+
     @Bean
     public DataPermissionInterceptor dataPermissionInterceptor() {
         return new DataPermissionInterceptor(redissonClient);
@@ -26,9 +36,13 @@ public class MyBatisPlusConfig {
     }
 
     @Bean
-    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+    public MybatisPlusInterceptor mybatisPlusInterceptor(TenantProperties tenantProperties) {
         MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
         //配置拦截器
+        if (Boolean.TRUE.equals(tenantProperties.getEnable())) {
+            // 启用多租户插件拦截
+            interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new MultiTenantHandler(tenantProperties,baseController)));
+        }
         interceptor.addInnerInterceptor(dataPermissionInterceptor());
         interceptor.addInnerInterceptor(paginationInnerInterceptor());
         return interceptor;

+ 23 - 0
commons/src/main/java/com/jzg/core/base/TenantContext.java

@@ -0,0 +1,23 @@
+package com.jzg.core.base;
+
+/**
+ * 租户上下文
+ *
+ * @author dongxin
+ */
+public class TenantContext {
+
+    private static final ThreadLocal<Boolean> THREAD_LOCAL = ThreadLocal.withInitial(() -> false);
+
+    public static void setInternalInvocation(boolean internal) {
+        THREAD_LOCAL.set(internal);
+    }
+
+    public static boolean isInternalInvocation() {
+        return THREAD_LOCAL.get();
+    }
+
+    public static void clear() {
+        THREAD_LOCAL.remove();
+    }
+}

+ 41 - 0
commons/src/main/java/com/jzg/entity/properties/TenantProperties.java

@@ -0,0 +1,41 @@
+package com.jzg.entity.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.List;
+
+/**
+ * 多租户配置属性类
+ *
+ * @author dongxin
+ */
+@Data
+@ConfigurationProperties(prefix = "tenant")
+public class TenantProperties {
+
+    /**
+     * 是否开启多租户
+     */
+    private Boolean enable;
+
+    /**
+     * 租户id字段名
+     */
+    private String column;
+
+    /**
+     * 需要进行租户id过滤的表名集合
+     */
+    private List<String> filterTables;
+
+    /**
+     * 需要忽略的多租户的表,此配置优先filterTables,若此配置为空则启用filterTables
+     */
+    private List<String> ignoreTables;
+
+    /**
+     * 需要排除租户过滤的登录用户名
+     */
+    private List<String> ignoreLoginNames;
+}

+ 7 - 0
platform/src/main/java/com/jzg/aop/aspect/LogAspect.java

@@ -2,12 +2,14 @@ package com.jzg.aop.aspect;
 
 import com.alibaba.fastjson2.JSON;
 import com.jzg.annotation.Log;
+import com.jzg.core.base.BaseController;
 import com.jzg.entity.enums.BusinessStatus;
 import com.jzg.entity.enums.HttpMethod;
 import com.jzg.entity.po.SysOperLog;
 import com.jzg.filter.PropertyPreExcludeFilter;
 import com.jzg.manager.AsyncManager;
 import com.jzg.manager.factory.AsyncFactory;
+import com.jzg.mapper.SysOperLogMapper;
 import com.jzg.util.ip.IpUtils;
 import com.jzg.util.ServletUtils;
 import com.jzg.util.StringUtils;
@@ -21,6 +23,7 @@ import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.NamedThreadLocal;
 import org.springframework.stereotype.Component;
 import org.springframework.validation.BindingResult;
@@ -46,6 +49,9 @@ public class LogAspect
     /** 计算操作消耗时间 */
     private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
 
+    @Autowired
+    private BaseController baseController;
+
     /**
      * 处理请求前执行
      */
@@ -83,6 +89,7 @@ public class LogAspect
         try
         {
             // 获取当前的用户
+//            String userName = baseController.getUserName();
 //            LoginUser loginUser = SecurityUtils.getLoginUser();
 
             // *========数据库日志=========*//

+ 1 - 0
platform/src/main/java/com/jzg/mapper/SysRoleMapper.java

@@ -1,6 +1,7 @@
 package com.jzg.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jzg.annotation.IgnoreTenant;
 import com.jzg.entity.po.SysRole;
 import com.jzg.entity.vo.RoleMenuVo;
 import org.apache.ibatis.annotations.Mapper;

+ 2 - 0
platform/src/main/java/com/jzg/service/impl/UserRoleServiceImpl.java

@@ -1,6 +1,7 @@
 package com.jzg.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.jzg.annotation.IgnoreTenant;
 import com.jzg.entity.po.SysRole;
 import com.jzg.entity.vo.RoleMenuVo;
 import com.jzg.mapper.SysRoleMapper;
@@ -11,6 +12,7 @@ import java.util.List;
 
 @Service
 public class UserRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements UserRoleService {
+    @IgnoreTenant
     @Override
     public List<RoleMenuVo> getAllRoleMenu() {
         List<RoleMenuVo> allRoleMenu = baseMapper.getAllRoleMenu();