当前位置:   article > 正文

mybatis sql拦截器配置多租户(极简版)_mybatis 多租户拦截器

mybatis 多租户拦截器

               任务目标:

                        利用sql拦截器对sql进行二次修改,自动携带租户查询

                废话少说,直接上代码

                 首先注册sql拦截器SqlInterceptor找不到没关系,这是我自己定义的

  1. package com.example.pidog.config;
  2. import com.example.pidog.interceptor.SqlInterceptor;
  3. import org.apache.ibatis.session.SqlSessionFactory;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.context.annotation.Configuration;
  6. import javax.annotation.PostConstruct;
  7. import java.util.List;
  8. @Configuration
  9. public class MybatisConfig {
  10. @Autowired
  11. private List<SqlSessionFactory> sqlSessionFactoryList;
  12. @PostConstruct
  13. public void addMySqlInterceptor() {
  14. SqlInterceptor interceptor = new SqlInterceptor();
  15. for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
  16. sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
  17. }
  18. }
  19. }
  1. package com.example.pidog.interceptor;
  2. import cn.hutool.core.collection.CollectionUtil;
  3. import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
  4. import com.example.pidog.handlers.TenantSqlHandle;
  5. import com.example.pidog.util.StaticMethodGetBean;
  6. import lombok.Setter;
  7. import lombok.experimental.Accessors;
  8. import org.apache.ibatis.executor.statement.StatementHandler;
  9. import org.apache.ibatis.mapping.BoundSql;
  10. import org.apache.ibatis.mapping.MappedStatement;
  11. import org.apache.ibatis.mapping.ParameterMapping;
  12. import org.apache.ibatis.mapping.SqlCommandType;
  13. import org.apache.ibatis.plugin.*;
  14. import org.apache.ibatis.reflection.MetaObject;
  15. import org.apache.ibatis.reflection.SystemMetaObject;
  16. import org.apache.ibatis.session.RowBounds;
  17. import javax.annotation.Resource;
  18. import java.lang.reflect.Field;
  19. import java.lang.reflect.Method;
  20. import java.sql.Connection;
  21. import java.util.List;
  22. @Setter
  23. @Accessors(chain = true)
  24. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
  25. public class SqlInterceptor implements Interceptor {
  26. @SuppressWarnings("unchecked")
  27. @Override
  28. public Object intercept(Invocation invocation) throws Throwable {
  29. StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
  30. Object parameter = invocation.getArgs()[1];
  31. System.out.println(parameter);
  32. MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
  33. MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
  34. String id = mappedStatement.getId();
  35. if (filterMethodById(id)) {
  36. BoundSql boundSql = statementHandler.getBoundSql();
  37. String sql = boundSql.getSql();//注意 这里是最终执行的sql 也就是我们接下来需要修改的sql
  38. sql = new TenantSqlHandle().tenantHandle(id,mappedStatement.getSqlCommandType(),sql);
  39. Field field = boundSql.getClass().getDeclaredField("sql");
  40. field.setAccessible(true);
  41. field.set(boundSql, sql);
  42. }
  43. return invocation.proceed();
  44. }
  45. @Override
  46. public Object plugin(Object target) {
  47. if (target instanceof StatementHandler) {
  48. return Plugin.wrap(target, this);
  49. }
  50. return target;
  51. }
  52. /**
  53. * 根据获取到执行 id 找到对应的方法
  54. *
  55. * @param id 根据 MappedStatement 获取到的 id 属性
  56. * @return 是否是 searchByQuery 方法
  57. */
  58. private boolean filterMethodById(String id) {
  59. String[] split = id.split("\\.");
  60. return CollectionUtil.contains(CollectionUtil.newArrayList("selectById", "updateById"), split[split.length - 1]);
  61. }
  62. }
  1. package com.example.pidog.handlers;
  2. import com.example.pidog.threadLocal.TenantThreadLocal;
  3. import javafx.util.Pair;
  4. import org.apache.ibatis.mapping.SqlCommandType;
  5. import org.springframework.beans.factory.annotation.Value;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.stereotype.Component;
  8. import java.lang.reflect.Method;
  9. import java.util.Locale;
  10. /**
  11. * 租户sql处理器
  12. */
  13. public class TenantSqlHandle {
  14. //这里没什么好说的 拼接修改sql字符串就是了
  15. public String tenantHandle(String id, SqlCommandType sqlCommandType, String sql) {
  16. if (TenantThreadLocal.getCurrentDept() == null) {
  17. return sql;
  18. }
  19. Pair<String, String> dept = TenantThreadLocal.getCurrentDept();
  20. if (sql.contains("WHERE")) {
  21. int whereIndexOf = sql.indexOf("WHERE");
  22. StringBuilder sb = new StringBuilder(sql);
  23. sb.insert(whereIndexOf + "WHERE".length(), " " + dept.getKey() + "=" + dept.getValue() + " and ");
  24. return sb.toString();
  25. }
  26. return sql;
  27. }
  28. }
  1. package com.example.pidog.threadLocal;
  2. import com.example.pidog.domain.User;
  3. import javafx.util.Pair;
  4. public class TenantThreadLocal {
  5. /**
  6. * 构造函数私有
  7. */
  8. private TenantThreadLocal() {
  9. }
  10. private static final ThreadLocal<Pair<String,String>> TENANT_THREAD_LOCAL =
  11. new ThreadLocal<>();
  12. /**
  13. * 清除租户信息
  14. */
  15. public static void clear() {
  16. TENANT_THREAD_LOCAL.remove();
  17. }
  18. /**
  19. * 存储当前租户
  20. */
  21. public static void set(Pair<String, String> dept) {
  22. TENANT_THREAD_LOCAL.set(dept);
  23. }
  24. /**
  25. * 获取当前租户id
  26. */
  27. public static Pair<String, String> getCurrentDept() {
  28. return TENANT_THREAD_LOCAL.get();
  29. }
  30. }

最后将租户的数据保存在线程变量里

接下来去请求拦截器里修改一下,自动从请求头取出租户id,放在线程变量中。

  1. package com.example.pidog.interceptor;
  2. import com.example.pidog.annotation.authority.AuthUtil;
  3. import com.example.pidog.annotation.authority.CrossAuth;
  4. import com.example.pidog.annotation.sql.CrossTenant;
  5. import com.example.pidog.annotation.sql.MultiTenant;
  6. import com.example.pidog.config.JwtConfig;
  7. import com.example.pidog.threadLocal.TenantThreadLocal;
  8. import io.jsonwebtoken.Claims;
  9. import io.jsonwebtoken.SignatureException;
  10. import javafx.util.Pair;
  11. import org.springframework.core.annotation.AnnotationUtils;
  12. import org.springframework.stereotype.Component;
  13. import org.springframework.util.StringUtils;
  14. import org.springframework.web.method.HandlerMethod;
  15. import org.springframework.web.servlet.HandlerInterceptor;
  16. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  17. import javax.annotation.Resource;
  18. import javax.servlet.http.HttpServletRequest;
  19. import javax.servlet.http.HttpServletResponse;
  20. import java.lang.reflect.Method;
  21. import java.util.Objects;
  22. @Component
  23. public class TokenInterceptor implements HandlerInterceptor {
  24. @Resource
  25. private AuthUtil authUtil;
  26. @Override
  27. public boolean preHandle(HttpServletRequest request,
  28. HttpServletResponse response,
  29. Object handler) throws SignatureException {
  30. Method method = ((HandlerMethod) handler).getMethod();
  31. if (method.isAnnotationPresent(CrossAuth.class)) {
  32. return true;
  33. }
  34. Claims claims = authUtil.examineToken(request);
  35. if (method.isAnnotationPresent(CrossTenant.class)) { //如果存在越过租户注解
  36. return true; //直接越过
  37. }
  38. if (claims.get("tenantId") == null) { //如果租户id为空
  39. throw new SignatureException("参数非法,权限校验失败");//抛出异常
  40. }
  41. TenantThreadLocal.set(new Pair<>("tenantId", claims.get("tenantId").toString())); //向线程变量中添加租户信息
  42. return true;
  43. }
  44. }

这是我的请求拦截器 核心在authUtil中

  1. package com.example.pidog.annotation.authority;
  2. import com.example.pidog.config.JwtConfig;
  3. import com.example.pidog.domain.User;
  4. import com.example.pidog.threadLocal.UserThreadLocal;
  5. import io.jsonwebtoken.Claims;
  6. import io.jsonwebtoken.SignatureException;
  7. import org.springframework.stereotype.Component;
  8. import org.springframework.util.StringUtils;
  9. import javax.annotation.Resource;
  10. import javax.servlet.http.HttpServletRequest;
  11. @Component
  12. public class AuthUtil {
  13. @Resource
  14. private JwtConfig jwtConfig;
  15. /**
  16. * 验证token合法性
  17. *
  18. * @param request
  19. */
  20. public Claims examineToken(HttpServletRequest request) {
  21. String token = request.getHeader(jwtConfig.getHeader());
  22. if (StringUtils.isEmpty(token)) {
  23. token = request.getParameter(jwtConfig.getHeader());
  24. }
  25. if (StringUtils.isEmpty(token)) {
  26. throw new SignatureException(jwtConfig.getHeader() + "不能为空");
  27. }
  28. try {
  29. Claims claims = jwtConfig.getTokenClaim(token);
  30. if (claims == null || jwtConfig.isTokenExpired(claims.getExpiration())) {
  31. throw new SignatureException(jwtConfig.getHeader() + "失效,请重新登录。");
  32. }
  33. if (claims.get("userId") == null) {
  34. throw new SignatureException(jwtConfig.getHeader() + "失效,请重新登录。");
  35. }
  36. if (claims.get("nickName") == null) {
  37. throw new SignatureException(jwtConfig.getHeader() + "失效,请重新登录。");
  38. }
  39. User user = new User();
  40. user.setUserId(claims.get("userId", Integer.class).longValue());
  41. user.setNickName(claims.get("nickName", String.class));
  42. if(!jwtConfig.comparisonCache(user.getUserId(), token)){
  43. throw new SignatureException(jwtConfig.getHeader() + "失效,请重新登录。");
  44. };
  45. UserThreadLocal.set(user);
  46. return claims;
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. throw new SignatureException(jwtConfig.getHeader() + "失效,请重新登录。");
  50. }
  51. }
  52. }

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/407539
推荐阅读
相关标签
  

闽ICP备14008679号