Commit 59234e1eeade300a68adc8183d58f616c14e90f1
1 parent
8500575f
feat:增加租户切换的能力
Showing
11 changed files
with
183 additions
and
11 deletions
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionRuleHandler.java
| @@ -12,6 +12,8 @@ import net.sf.jsqlparser.schema.Table; | @@ -12,6 +12,8 @@ import net.sf.jsqlparser.schema.Table; | ||
| 12 | 12 | ||
| 13 | import java.util.List; | 13 | import java.util.List; |
| 14 | 14 | ||
| 15 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.skipPermissionCheck; | ||
| 16 | + | ||
| 15 | /** | 17 | /** |
| 16 | * 基于 {@link DataPermissionRule} 的数据权限处理器 | 18 | * 基于 {@link DataPermissionRule} 的数据权限处理器 |
| 17 | * | 19 | * |
| @@ -27,6 +29,11 @@ public class DataPermissionRuleHandler implements MultiDataPermissionHandler { | @@ -27,6 +29,11 @@ public class DataPermissionRuleHandler implements MultiDataPermissionHandler { | ||
| 27 | 29 | ||
| 28 | @Override | 30 | @Override |
| 29 | public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) { | 31 | public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) { |
| 32 | + // 特殊:跨租户访问 | ||
| 33 | + if (skipPermissionCheck()) { | ||
| 34 | + return null; | ||
| 35 | + } | ||
| 36 | + | ||
| 30 | // 获得 Mapper 对应的数据权限的规则 | 37 | // 获得 Mapper 对应的数据权限的规则 |
| 31 | List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(mappedStatementId); | 38 | List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(mappedStatementId); |
| 32 | if (CollUtil.isEmpty(rules)) { | 39 | if (CollUtil.isEmpty(rules)) { |
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java
| @@ -32,13 +32,12 @@ public class DataPermissionUtils { | @@ -32,13 +32,12 @@ public class DataPermissionUtils { | ||
| 32 | * @param runnable 逻辑 | 32 | * @param runnable 逻辑 |
| 33 | */ | 33 | */ |
| 34 | public static void executeIgnore(Runnable runnable) { | 34 | public static void executeIgnore(Runnable runnable) { |
| 35 | - DataPermission dataPermission = getDisableDataPermissionDisable(); | ||
| 36 | - DataPermissionContextHolder.add(dataPermission); | 35 | + addDisableDataPermission(); |
| 37 | try { | 36 | try { |
| 38 | // 执行 runnable | 37 | // 执行 runnable |
| 39 | runnable.run(); | 38 | runnable.run(); |
| 40 | } finally { | 39 | } finally { |
| 41 | - DataPermissionContextHolder.remove(); | 40 | + removeDataPermission(); |
| 42 | } | 41 | } |
| 43 | } | 42 | } |
| 44 | 43 | ||
| @@ -50,14 +49,25 @@ public class DataPermissionUtils { | @@ -50,14 +49,25 @@ public class DataPermissionUtils { | ||
| 50 | */ | 49 | */ |
| 51 | @SneakyThrows | 50 | @SneakyThrows |
| 52 | public static <T> T executeIgnore(Callable<T> callable) { | 51 | public static <T> T executeIgnore(Callable<T> callable) { |
| 53 | - DataPermission dataPermission = getDisableDataPermissionDisable(); | ||
| 54 | - DataPermissionContextHolder.add(dataPermission); | 52 | + addDisableDataPermission(); |
| 55 | try { | 53 | try { |
| 56 | // 执行 callable | 54 | // 执行 callable |
| 57 | return callable.call(); | 55 | return callable.call(); |
| 58 | } finally { | 56 | } finally { |
| 59 | - DataPermissionContextHolder.remove(); | 57 | + removeDataPermission(); |
| 60 | } | 58 | } |
| 61 | } | 59 | } |
| 62 | 60 | ||
| 61 | + /** | ||
| 62 | + * 添加忽略数据权限 | ||
| 63 | + */ | ||
| 64 | + public static void addDisableDataPermission(){ | ||
| 65 | + DataPermission dataPermission = getDisableDataPermissionDisable(); | ||
| 66 | + DataPermissionContextHolder.add(dataPermission); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + public static void removeDataPermission(){ | ||
| 70 | + DataPermissionContextHolder.remove(); | ||
| 71 | + } | ||
| 72 | + | ||
| 63 | } | 73 | } |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java
| @@ -34,6 +34,13 @@ public class TenantProperties { | @@ -34,6 +34,13 @@ public class TenantProperties { | ||
| 34 | private Set<String> ignoreUrls = new HashSet<>(); | 34 | private Set<String> ignoreUrls = new HashSet<>(); |
| 35 | 35 | ||
| 36 | /** | 36 | /** |
| 37 | + * 需要忽略跨(切换)租户访问的请求 | ||
| 38 | + * | ||
| 39 | + * 原因是:某些接口,访问的是个人信息,在跨租户是获取不到的! | ||
| 40 | + */ | ||
| 41 | + private Set<String> ignoreVisitUrls = Collections.emptySet(); | ||
| 42 | + | ||
| 43 | + /** | ||
| 37 | * 需要忽略多租户的表 | 44 | * 需要忽略多租户的表 |
| 38 | * | 45 | * |
| 39 | * 即默认所有表都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟 | 46 | * 即默认所有表都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟 |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
| @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.tenant.config; | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.tenant.config; | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; | 4 | import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; |
| 5 | import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; | 5 | import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; |
| 6 | +import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService; | ||
| 6 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; | 7 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| 7 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; | 8 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; |
| 8 | import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; | 9 | import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; |
| @@ -15,6 +16,7 @@ import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; | @@ -15,6 +16,7 @@ import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; | ||
| 15 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; | 16 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; |
| 16 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkServiceImpl; | 17 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkServiceImpl; |
| 17 | import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter; | 18 | import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter; |
| 19 | +import cn.iocoder.yudao.framework.tenant.core.web.TenantVisitContextInterceptor; | ||
| 18 | import cn.iocoder.yudao.framework.web.config.WebProperties; | 20 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 19 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; | 21 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; |
| 20 | import cn.iocoder.yudao.module.system.api.tenant.TenantApi; | 22 | import cn.iocoder.yudao.module.system.api.tenant.TenantApi; |
| @@ -36,6 +38,8 @@ import org.springframework.data.redis.cache.RedisCacheWriter; | @@ -36,6 +38,8 @@ import org.springframework.data.redis.cache.RedisCacheWriter; | ||
| 36 | import org.springframework.data.redis.connection.RedisConnectionFactory; | 38 | import org.springframework.data.redis.connection.RedisConnectionFactory; |
| 37 | import org.springframework.data.redis.core.RedisTemplate; | 39 | import org.springframework.data.redis.core.RedisTemplate; |
| 38 | import org.springframework.web.method.HandlerMethod; | 40 | import org.springframework.web.method.HandlerMethod; |
| 41 | +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
| 42 | +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
| 39 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo; | 43 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo; |
| 40 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; | 44 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; |
| 41 | import org.springframework.web.util.pattern.PathPattern; | 45 | import org.springframework.web.util.pattern.PathPattern; |
| @@ -115,6 +119,25 @@ public class YudaoTenantAutoConfiguration { | @@ -115,6 +119,25 @@ public class YudaoTenantAutoConfiguration { | ||
| 115 | } | 119 | } |
| 116 | } | 120 | } |
| 117 | 121 | ||
| 122 | + @Bean | ||
| 123 | + public TenantVisitContextInterceptor tenantVisitContextInterceptor(TenantProperties tenantProperties, | ||
| 124 | + SecurityFrameworkService securityFrameworkService) { | ||
| 125 | + return new TenantVisitContextInterceptor(tenantProperties, securityFrameworkService); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Bean | ||
| 129 | + public WebMvcConfigurer tenantWebMvcConfigurer(TenantProperties tenantProperties, | ||
| 130 | + TenantVisitContextInterceptor tenantVisitContextInterceptor) { | ||
| 131 | + return new WebMvcConfigurer() { | ||
| 132 | + | ||
| 133 | + @Override | ||
| 134 | + public void addInterceptors(InterceptorRegistry registry) { | ||
| 135 | + registry.addInterceptor(tenantVisitContextInterceptor) | ||
| 136 | + .excludePathPatterns(tenantProperties.getIgnoreVisitUrls().toArray(new String[0])); | ||
| 137 | + } | ||
| 138 | + }; | ||
| 139 | + } | ||
| 140 | + | ||
| 118 | // ========== Security ========== | 141 | // ========== Security ========== |
| 119 | 142 | ||
| 120 | @Bean | 143 | @Bean |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantVisitContextInterceptor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.web; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ObjUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | ||
| 5 | +import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 6 | +import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService; | ||
| 7 | +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | ||
| 8 | +import cn.iocoder.yudao.framework.tenant.config.TenantProperties; | ||
| 9 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||
| 10 | +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | ||
| 11 | +import jakarta.servlet.http.HttpServletRequest; | ||
| 12 | +import jakarta.servlet.http.HttpServletResponse; | ||
| 13 | +import lombok.RequiredArgsConstructor; | ||
| 14 | +import lombok.extern.slf4j.Slf4j; | ||
| 15 | +import org.springframework.web.servlet.HandlerInterceptor; | ||
| 16 | + | ||
| 17 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; | ||
| 18 | + | ||
| 19 | +@RequiredArgsConstructor | ||
| 20 | +@Slf4j | ||
| 21 | +public class TenantVisitContextInterceptor implements HandlerInterceptor { | ||
| 22 | + | ||
| 23 | + private static final String PERMISSION = "system:tenant:visit"; | ||
| 24 | + | ||
| 25 | + private final TenantProperties tenantProperties; | ||
| 26 | + | ||
| 27 | + private final SecurityFrameworkService securityFrameworkService; | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { | ||
| 31 | + // 如果和当前租户编号一致,则直接跳过 | ||
| 32 | + Long visitTenantId = WebFrameworkUtils.getVisitTenantId(request); | ||
| 33 | + if (visitTenantId == null) { | ||
| 34 | + return true; | ||
| 35 | + } | ||
| 36 | + if (ObjUtil.equal(visitTenantId, TenantContextHolder.getTenantId())) { | ||
| 37 | + return true; | ||
| 38 | + } | ||
| 39 | + // 必须是登录用户 | ||
| 40 | + LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); | ||
| 41 | + if (loginUser == null) { | ||
| 42 | + return true; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + // 校验用户是否可切换租户 | ||
| 46 | + if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) { | ||
| 47 | + throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), "您无权切换租户"); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + // 【重点】切换租户编号 | ||
| 51 | + loginUser.setVisitTenantId(visitTenantId); | ||
| 52 | + TenantContextHolder.setTenantId(visitTenantId); | ||
| 53 | + return true; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @Override | ||
| 57 | + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { | ||
| 58 | + // 【重点】清理切换,换回原租户编号 | ||
| 59 | + LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); | ||
| 60 | + if (loginUser != null && loginUser.getTenantId() != null) { | ||
| 61 | + TenantContextHolder.setTenantId(loginUser.getTenantId()); | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | +} |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java
| @@ -56,6 +56,10 @@ public class LoginUser { | @@ -56,6 +56,10 @@ public class LoginUser { | ||
| 56 | */ | 56 | */ |
| 57 | @JsonIgnore | 57 | @JsonIgnore |
| 58 | private Map<String, Object> context; | 58 | private Map<String, Object> context; |
| 59 | + /** | ||
| 60 | + * 访问的租户编号 | ||
| 61 | + */ | ||
| 62 | + private Long visitTenantId; | ||
| 59 | 63 | ||
| 60 | public void setContext(String key, Object value) { | 64 | public void setContext(String key, Object value) { |
| 61 | if (context == null) { | 65 | if (context == null) { |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkServiceImpl.java
| @@ -9,6 +9,7 @@ import lombok.AllArgsConstructor; | @@ -9,6 +9,7 @@ import lombok.AllArgsConstructor; | ||
| 9 | import java.util.Arrays; | 9 | import java.util.Arrays; |
| 10 | 10 | ||
| 11 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | 11 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 12 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.skipPermissionCheck; | ||
| 12 | 13 | ||
| 13 | /** | 14 | /** |
| 14 | * 默认的 {@link SecurityFrameworkService} 实现类 | 15 | * 默认的 {@link SecurityFrameworkService} 实现类 |
| @@ -27,6 +28,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { | @@ -27,6 +28,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { | ||
| 27 | 28 | ||
| 28 | @Override | 29 | @Override |
| 29 | public boolean hasAnyPermissions(String... permissions) { | 30 | public boolean hasAnyPermissions(String... permissions) { |
| 31 | + // 特殊:跨租户访问 | ||
| 32 | + if (skipPermissionCheck()) { | ||
| 33 | + return true; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + // 权限校验 | ||
| 30 | Long userId = getLoginUserId(); | 37 | Long userId = getLoginUserId(); |
| 31 | if (userId == null) { | 38 | if (userId == null) { |
| 32 | return false; | 39 | return false; |
| @@ -41,6 +48,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { | @@ -41,6 +48,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { | ||
| 41 | 48 | ||
| 42 | @Override | 49 | @Override |
| 43 | public boolean hasAnyRoles(String... roles) { | 50 | public boolean hasAnyRoles(String... roles) { |
| 51 | + // 特殊:跨租户访问 | ||
| 52 | + if (skipPermissionCheck()) { | ||
| 53 | + return true; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + // 权限校验 | ||
| 44 | Long userId = getLoginUserId(); | 57 | Long userId = getLoginUserId(); |
| 45 | if (userId == null) { | 58 | if (userId == null) { |
| 46 | return false; | 59 | return false; |
| @@ -55,6 +68,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { | @@ -55,6 +68,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { | ||
| 55 | 68 | ||
| 56 | @Override | 69 | @Override |
| 57 | public boolean hasAnyScopes(String... scope) { | 70 | public boolean hasAnyScopes(String... scope) { |
| 71 | + // 特殊:跨租户访问 | ||
| 72 | + if (skipPermissionCheck()) { | ||
| 73 | + return true; | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + // 权限校验 | ||
| 58 | LoginUser user = SecurityFrameworkUtils.getLoginUser(); | 77 | LoginUser user = SecurityFrameworkUtils.getLoginUser(); |
| 59 | if (user == null) { | 78 | if (user == null) { |
| 60 | return false; | 79 | return false; |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java
| 1 | package cn.iocoder.yudao.framework.security.core.util; | 1 | package cn.iocoder.yudao.framework.security.core.util; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.map.MapUtil; | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | +import cn.hutool.core.util.ObjUtil; | ||
| 4 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | import cn.iocoder.yudao.framework.security.core.LoginUser; | 6 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 6 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | 7 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| @@ -137,4 +138,21 @@ public class SecurityFrameworkUtils { | @@ -137,4 +138,21 @@ public class SecurityFrameworkUtils { | ||
| 137 | return authenticationToken; | 138 | return authenticationToken; |
| 138 | } | 139 | } |
| 139 | 140 | ||
| 141 | + /** | ||
| 142 | + * 是否条件跳过权限校验,包括数据权限、功能权限 | ||
| 143 | + * | ||
| 144 | + * @return 是否跳过 | ||
| 145 | + */ | ||
| 146 | + public static boolean skipPermissionCheck() { | ||
| 147 | + LoginUser loginUser = getLoginUser(); | ||
| 148 | + if (loginUser == null) { | ||
| 149 | + return false; | ||
| 150 | + } | ||
| 151 | + if (loginUser.getVisitTenantId() == null) { | ||
| 152 | + return false; | ||
| 153 | + } | ||
| 154 | + // 重点:跨租户访问时,无法进行权限校验 | ||
| 155 | + return ObjUtil.notEqual(loginUser.getVisitTenantId(), loginUser.getTenantId()); | ||
| 156 | + } | ||
| 157 | + | ||
| 140 | } | 158 | } |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
| 1 | package cn.iocoder.yudao.framework.web.core.util; | 1 | package cn.iocoder.yudao.framework.web.core.util; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.NumberUtil; | 3 | import cn.hutool.core.util.NumberUtil; |
| 4 | -import cn.hutool.extra.servlet.ServletUtil; | ||
| 5 | import cn.iocoder.yudao.framework.common.enums.TerminalEnum; | 4 | import cn.iocoder.yudao.framework.common.enums.TerminalEnum; |
| 6 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | 5 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 7 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 8 | -import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | ||
| 9 | import cn.iocoder.yudao.framework.web.config.WebProperties; | 7 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 8 | +import jakarta.servlet.ServletRequest; | ||
| 9 | +import jakarta.servlet.http.HttpServletRequest; | ||
| 10 | import org.springframework.web.context.request.RequestAttributes; | 10 | import org.springframework.web.context.request.RequestAttributes; |
| 11 | import org.springframework.web.context.request.RequestContextHolder; | 11 | import org.springframework.web.context.request.RequestContextHolder; |
| 12 | import org.springframework.web.context.request.ServletRequestAttributes; | 12 | import org.springframework.web.context.request.ServletRequestAttributes; |
| 13 | 13 | ||
| 14 | -import jakarta.servlet.ServletRequest; | ||
| 15 | -import jakarta.servlet.http.HttpServletRequest; | ||
| 16 | - | ||
| 17 | /** | 14 | /** |
| 18 | * 专属于 web 包的工具类 | 15 | * 专属于 web 包的工具类 |
| 19 | * | 16 | * |
| @@ -27,6 +24,7 @@ public class WebFrameworkUtils { | @@ -27,6 +24,7 @@ public class WebFrameworkUtils { | ||
| 27 | private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = "common_result"; | 24 | private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = "common_result"; |
| 28 | 25 | ||
| 29 | public static final String HEADER_TENANT_ID = "tenant-id"; | 26 | public static final String HEADER_TENANT_ID = "tenant-id"; |
| 27 | + public static final String HEADER_VISIT_TENANT_ID = "visit-tenant-id"; | ||
| 30 | 28 | ||
| 31 | /** | 29 | /** |
| 32 | * 终端的 Header | 30 | * 终端的 Header |
| @@ -53,6 +51,18 @@ public class WebFrameworkUtils { | @@ -53,6 +51,18 @@ public class WebFrameworkUtils { | ||
| 53 | return NumberUtil.isNumber(tenantId) ? Long.valueOf(tenantId) : null; | 51 | return NumberUtil.isNumber(tenantId) ? Long.valueOf(tenantId) : null; |
| 54 | } | 52 | } |
| 55 | 53 | ||
| 54 | + /** | ||
| 55 | + * 获得访问的租户编号,从 header 中 | ||
| 56 | + * 考虑到其它 framework 组件也会使用到租户编号,所以不得不放在 WebFrameworkUtils 统一提供 | ||
| 57 | + * | ||
| 58 | + * @param request 请求 | ||
| 59 | + * @return 租户编号 | ||
| 60 | + */ | ||
| 61 | + public static Long getVisitTenantId(HttpServletRequest request) { | ||
| 62 | + String tenantId = request.getHeader(HEADER_VISIT_TENANT_ID); | ||
| 63 | + return NumberUtil.isNumber(tenantId)? Long.valueOf(tenantId) : null; | ||
| 64 | + } | ||
| 65 | + | ||
| 56 | public static void setLoginUserId(ServletRequest request, Long userId) { | 66 | public static void setLoginUserId(ServletRequest request, Long userId) { |
| 57 | request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId); | 67 | request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId); |
| 58 | } | 68 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http
| @@ -3,3 +3,9 @@ GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10 | @@ -3,3 +3,9 @@ GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10 | ||
| 3 | Authorization: Bearer {{token}} | 3 | Authorization: Bearer {{token}} |
| 4 | #Authorization: Bearer test100 | 4 | #Authorization: Bearer test100 |
| 5 | tenant-id: {{adminTenantId}} | 5 | tenant-id: {{adminTenantId}} |
| 6 | + | ||
| 7 | +### 请求 /system/user/page 接口(测试访问别的租户) | ||
| 8 | +GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10 | ||
| 9 | +Authorization: Bearer {{token}} | ||
| 10 | +tenant-id: {{adminTenantId}} | ||
| 11 | +visit-tenant-id: 122 | ||
| 6 | \ No newline at end of file | 12 | \ No newline at end of file |
yudao-server/src/main/resources/application.yaml
| @@ -274,6 +274,9 @@ yudao: | @@ -274,6 +274,9 @@ yudao: | ||
| 274 | enable: true | 274 | enable: true |
| 275 | ignore-urls: | 275 | ignore-urls: |
| 276 | - /jmreport/* # 积木报表,无法携带租户编号 | 276 | - /jmreport/* # 积木报表,无法携带租户编号 |
| 277 | + ignore-visit-urls: | ||
| 278 | + - /admin-api/system/user/profile/** | ||
| 279 | + - /admin-api/system/auth/** | ||
| 277 | ignore-tables: | 280 | ignore-tables: |
| 278 | ignore-caches: | 281 | ignore-caches: |
| 279 | - user_role_ids | 282 | - user_role_ids |