diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java index 8fe59c3a3..4ca9b7422 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.framework.tenant.config; import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.biz.system.tenant.TenantCommonApi; import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; @@ -8,7 +9,6 @@ import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; -import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect; import cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq.TenantRabbitMQInitializer; import cn.iocoder.yudao.framework.tenant.core.mq.redis.TenantRedisMessageInterceptor; import cn.iocoder.yudao.framework.tenant.core.mq.rocketmq.TenantRocketMQInitializer; @@ -20,7 +20,6 @@ import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter; import cn.iocoder.yudao.framework.tenant.core.web.TenantVisitContextInterceptor; import cn.iocoder.yudao.framework.web.config.WebProperties; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; -import cn.iocoder.yudao.framework.common.biz.system.tenant.TenantCommonApi; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import jakarta.annotation.Resource; @@ -46,8 +45,10 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.util.pattern.PathPattern; +import java.util.HashSet; import java.util.Map; import java.util.Objects; +import java.util.Set; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -93,41 +94,13 @@ public class YudaoTenantAutoConfiguration { // ========== WEB ========== @Bean - public FilterRegistrationBean tenantContextWebFilter(TenantProperties tenantProperties) { + public FilterRegistrationBean tenantContextWebFilter() { FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new TenantContextWebFilter()); registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER); - addIgnoreUrls(tenantProperties); return registrationBean; } - /** - * 如果 Controller 接口上,有 {@link TenantIgnore} 注解,那么添加到忽略的 URL 中 - * - * @param tenantProperties 租户配置 - */ - private void addIgnoreUrls(TenantProperties tenantProperties) { - // 获得接口对应的 HandlerMethod 集合 - RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) - applicationContext.getBean("requestMappingHandlerMapping"); - Map handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods(); - // 获得有 @TenantIgnore 注解的接口 - for (Map.Entry entry : handlerMethodMap.entrySet()) { - HandlerMethod handlerMethod = entry.getValue(); - if (!handlerMethod.hasMethodAnnotation(TenantIgnore.class)) { - continue; - } - // 添加到忽略的 URL 中 - if (entry.getKey().getPatternsCondition() != null) { - tenantProperties.getIgnoreUrls().addAll(entry.getKey().getPatternsCondition().getPatterns()); - } - if (entry.getKey().getPathPatternsCondition() != null) { - tenantProperties.getIgnoreUrls().addAll( - convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString)); - } - } - } - @Bean public TenantVisitContextInterceptor tenantVisitContextInterceptor(TenantProperties tenantProperties, SecurityFrameworkService securityFrameworkService) { @@ -155,18 +128,40 @@ public class YudaoTenantAutoConfiguration { GlobalExceptionHandler globalExceptionHandler, TenantFrameworkService tenantFrameworkService) { FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, webProperties, + registrationBean.setFilter(new TenantSecurityWebFilter(webProperties, tenantProperties, getTenantIgnoreUrls(), globalExceptionHandler, tenantFrameworkService)); registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER); return registrationBean; } - // ========== Job ========== - - @Bean - @ConditionalOnClass(name = "com.xxl.job.core.handler.annotation.XxlJob") - public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) { - return new TenantJobAspect(tenantFrameworkService); + /** + * 如果 Controller 接口上,有 {@link TenantIgnore} 注解,则添加到忽略租户的 URL 集合中 + * + * @return 忽略租户的 URL 集合 + */ + private Set getTenantIgnoreUrls() { + Set ignoreUrls = new HashSet<>(); + // 获得接口对应的 HandlerMethod 集合 + RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) + applicationContext.getBean("requestMappingHandlerMapping"); + Map handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods(); + // 获得有 @TenantIgnore 注解的接口 + for (Map.Entry entry : handlerMethodMap.entrySet()) { + HandlerMethod handlerMethod = entry.getValue(); + if (!handlerMethod.hasMethodAnnotation(TenantIgnore.class) // 方法级 + && !handlerMethod.getBeanType().isAnnotationPresent(TenantIgnore.class)) { // 接口级 + continue; + } + // 添加到忽略的 URL 中 + if (entry.getKey().getPatternsCondition() != null) { + ignoreUrls.addAll(entry.getKey().getPatternsCondition().getPatterns()); + } + if (entry.getKey().getPathPatternsCondition() != null) { + ignoreUrls.addAll( + convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString)); + } + } + return ignoreUrls; } // ========== MQ ========== diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java index d3f70b07e..b57383aef 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java @@ -12,15 +12,16 @@ import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; import cn.iocoder.yudao.framework.web.config.WebProperties; import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.AntPathMatcher; - import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.AntPathMatcher; + import java.io.IOException; import java.util.Objects; +import java.util.Set; /** * 多租户 Security Web 过滤器 @@ -35,17 +36,26 @@ public class TenantSecurityWebFilter extends ApiRequestFilter { private final TenantProperties tenantProperties; + /** + * 允许忽略租户的 URL 列表 + * + * 目的:解决 修改配置会导致 @TenantIgnore Controller 接口过滤失效 + */ + private final Set ignoreUrls; + private final AntPathMatcher pathMatcher; private final GlobalExceptionHandler globalExceptionHandler; private final TenantFrameworkService tenantFrameworkService; - public TenantSecurityWebFilter(TenantProperties tenantProperties, - WebProperties webProperties, + public TenantSecurityWebFilter(WebProperties webProperties, + TenantProperties tenantProperties, + Set ignoreUrls, GlobalExceptionHandler globalExceptionHandler, TenantFrameworkService tenantFrameworkService) { super(webProperties); this.tenantProperties = tenantProperties; + this.ignoreUrls = ignoreUrls; this.pathMatcher = new AntPathMatcher(); this.globalExceptionHandler = globalExceptionHandler; this.tenantFrameworkService = tenantFrameworkService; @@ -103,7 +113,8 @@ public class TenantSecurityWebFilter extends ApiRequestFilter { private boolean isIgnoreUrl(HttpServletRequest request) { String apiUri = request.getRequestURI().substring(request.getContextPath().length()); // 快速匹配,保证性能 - if (CollUtil.contains(tenantProperties.getIgnoreUrls(), apiUri)) { + if (CollUtil.contains(tenantProperties.getIgnoreUrls(), apiUri) + || CollUtil.contains(ignoreUrls, apiUri)) { return true; } // 逐个 Ant 路径匹配 @@ -112,6 +123,11 @@ public class TenantSecurityWebFilter extends ApiRequestFilter { return true; } } + for (String url : ignoreUrls) { + if (pathMatcher.match(url, apiUri)) { + return true; + } + } return false; } diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java index 8c2d93b22..6172bf6fd 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java @@ -165,7 +165,8 @@ public class YudaoWebSecurityConfigurerAdapter { // 获得有 @PermitAll 注解的接口 for (Map.Entry entry : handlerMethodMap.entrySet()) { HandlerMethod handlerMethod = entry.getValue(); - if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) { + if (!handlerMethod.hasMethodAnnotation(PermitAll.class) // 方法级 + && !handlerMethod.getBeanType().isAnnotationPresent(PermitAll.class)) { // 接口级 continue; } Set urls = new HashSet<>(); diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java index a66a58ac3..047fe5ffc 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.core.enums; import cn.hutool.core.util.ArrayUtil; import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import lombok.AllArgsConstructor; import lombok.Getter; @@ -57,7 +58,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable { /** * 不进行 reply 回复的方法集合 */ - public static final Set REPLY_DISABLED = Set.of( + public static final Set REPLY_DISABLED = SetUtils.asSet( STATE_UPDATE.getMethod(), OTA_PROGRESS.getMethod() // 参考阿里云,OTA 升级进度上报,不进行回复 ); diff --git a/yudao-module-pay/yudao-module-pay-server/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java b/yudao-module-pay/yudao-module-pay-server/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java index efcf58933..8f3541129 100755 --- a/yudao-module-pay/yudao-module-pay-server/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-server/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java @@ -610,7 +610,7 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest { orderExtensionMapper.insert(orderExtension); // 重要:需要将 order 的 extensionId 更新下 order.setExtensionId(orderExtension.getId()); - orderMapper.updateById(order); + orderMapper.updateById(new PayOrderDO().setId(order.getId()).setExtensionId(orderExtension.getId())); // 准备参数 PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, @@ -622,7 +622,7 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest { // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); // 断言 PayOrderDO :数据未更新,因为它是 SUCCESS - assertPojoEquals(order, orderMapper.selectOne(null)); + assertPojoEquals(order, orderMapper.selectOne(null), "updateTime", "updater"); // 断言,调用 verify(notifyService, never()).createPayNotifyTask(anyInt(), anyLong()); } @@ -664,7 +664,7 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest { "updateTime", "updater"); // 断言,调用 verify(notifyService).createPayNotifyTask(eq(PayNotifyTypeEnum.ORDER.getType()), - eq(orderExtension.getOrderId())); + eq(orderExtension.getOrderId())); } @Test