Prechádzať zdrojové kódy

reactor:@TenantIgnore 添加在 Controller 时,自动添加到 TenantProperties 中

YunaiV 6 mesiacov pred
rodič
commit
be86cdfd51

+ 42 - 1
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;
 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;
+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;
@@ -19,11 +20,13 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
 import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
+import jakarta.annotation.Resource;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Primary;
 import org.springframework.data.redis.cache.BatchStrategies;
@@ -32,14 +35,24 @@ import org.springframework.data.redis.cache.RedisCacheManager;
 import org.springframework.data.redis.cache.RedisCacheWriter;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.web.method.HandlerMethod;
+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.Map;
 import java.util.Objects;
 
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+
 @AutoConfiguration
 @ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户
 @EnableConfigurationProperties(TenantProperties.class)
 public class YudaoTenantAutoConfiguration {
 
+    @Resource
+    private ApplicationContext applicationContext;
+
     @Bean
     public TenantFrameworkService tenantFrameworkService(TenantApi tenantApi) {
         return new TenantFrameworkServiceImpl(tenantApi);
@@ -67,13 +80,41 @@ public class YudaoTenantAutoConfiguration {
     // ========== WEB ==========
 
     @Bean
-    public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() {
+    public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter(TenantProperties tenantProperties) {
         FilterRegistrationBean<TenantContextWebFilter> 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<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
+        // 获得有 @TenantIgnore 注解的接口
+        for (Map.Entry<RequestMappingInfo, HandlerMethod> 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));
+            }
+        }
+    }
+
     // ========== Security ==========
 
     @Bean

+ 2 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java

@@ -6,6 +6,7 @@ import cn.hutool.core.util.URLUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*;
 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
 import cn.iocoder.yudao.module.infra.service.file.FileService;
@@ -76,6 +77,7 @@ public class FileController {
 
     @GetMapping("/{configId}/get/**")
     @PermitAll
+    @TenantIgnore
     @Operation(summary = "下载文件")
     @Parameter(name = "configId", description = "配置编号", required = true)
     public void getFileContent(HttpServletRequest request,

+ 23 - 20
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mp.controller.admin.open;
 
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenCheckSignatureReqVO;
 import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenHandleMessageReqVO;
@@ -35,26 +36,6 @@ public class MpOpenController {
     @Resource
     private MpAccountService mpAccountService;
 
-    /**
-     * 接收微信公众号的校验签名
-     *
-     * 对应 <a href="https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html">文档</a>
-     */
-    @Operation(summary = "校验签名") // 参见
-    @GetMapping(value = "/{appId}", produces = "text/plain;charset=utf-8")
-    public String checkSignature(@PathVariable("appId") String appId,
-                                 MpOpenCheckSignatureReqVO reqVO) {
-        log.info("[checkSignature][appId({}) 接收到来自微信服务器的认证消息({})]", appId, reqVO);
-        // 校验请求签名
-        WxMpService wxMpService = mpServiceFactory.getRequiredMpService(appId);
-        // 校验通过
-        if (wxMpService.checkSignature(reqVO.getTimestamp(), reqVO.getNonce(), reqVO.getSignature())) {
-            return reqVO.getEchostr();
-        }
-        // 校验不通过
-        return "非法请求";
-    }
-
     /**
      * 接收微信公众号的消息推送
      *
@@ -62,6 +43,7 @@ public class MpOpenController {
      */
     @Operation(summary = "处理消息")
     @PostMapping(value = "/{appId}", produces = "application/xml; charset=UTF-8")
+    @TenantIgnore
     public String handleMessage(@PathVariable("appId") String appId,
                                 @RequestBody String content,
                                 MpOpenHandleMessageReqVO reqVO) {
@@ -79,6 +61,27 @@ public class MpOpenController {
         }
     }
 
+    /**
+     * 接收微信公众号的校验签名
+     *
+     * 对应 <a href="https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html">文档</a>
+     */
+    @Operation(summary = "校验签名") // 参见
+    @GetMapping(value = "/{appId}", produces = "text/plain;charset=utf-8")
+    @TenantIgnore
+    public String checkSignature(@PathVariable("appId") String appId,
+                                 MpOpenCheckSignatureReqVO reqVO) {
+        log.info("[checkSignature][appId({}) 接收到来自微信服务器的认证消息({})]", appId, reqVO);
+        // 校验请求签名
+        WxMpService wxMpService = mpServiceFactory.getRequiredMpService(appId);
+        // 校验通过
+        if (wxMpService.checkSignature(reqVO.getTimestamp(), reqVO.getNonce(), reqVO.getSignature())) {
+            return reqVO.getEchostr();
+        }
+        // 校验不通过
+        return "非法请求";
+    }
+
     private String handleMessage0(String appId, String content, MpOpenHandleMessageReqVO reqVO) {
         // 校验请求签名
         WxMpService mppService = mpServiceFactory.getRequiredMpService(appId);

+ 4 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO;
@@ -62,6 +63,7 @@ public class PayNotifyController {
     @PostMapping(value = "/order/{channelId}")
     @Operation(summary = "支付渠道的统一【支付】回调")
     @PermitAll
+    @TenantIgnore
     public String notifyOrder(@PathVariable("channelId") Long channelId,
                               @RequestParam(required = false) Map<String, String> params,
                               @RequestBody(required = false) String body) {
@@ -82,6 +84,7 @@ public class PayNotifyController {
     @PostMapping(value = "/refund/{channelId}")
     @Operation(summary = "支付渠道的统一【退款】回调")
     @PermitAll
+    @TenantIgnore
     public String notifyRefund(@PathVariable("channelId") Long channelId,
                                @RequestParam(required = false) Map<String, String> params,
                                @RequestBody(required = false) String body) {
@@ -102,6 +105,7 @@ public class PayNotifyController {
     @PostMapping(value = "/transfer/{channelId}")
     @Operation(summary = "支付渠道的统一【转账】回调")
     @PermitAll
+    @TenantIgnore
     public String notifyTransfer(@PathVariable("channelId") Long channelId,
                                  @RequestParam(required = false) Map<String, String> params,
                                  @RequestBody(required = false) String body) {

+ 3 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.controller.admin.captcha;
 
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import com.anji.captcha.model.common.ResponseModel;
 import com.anji.captcha.model.vo.CaptchaVO;
 import com.anji.captcha.service.CaptchaService;
@@ -26,6 +27,7 @@ public class CaptchaController {
     @PostMapping({"/get"})
     @Operation(summary = "获得验证码")
     @PermitAll
+    @TenantIgnore
     public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) {
         assert request.getRemoteHost() != null;
         data.setBrowserInfo(getRemoteId(request));
@@ -35,6 +37,7 @@ public class CaptchaController {
     @PostMapping("/check")
     @Operation(summary = "校验验证码")
     @PermitAll
+    @TenantIgnore
     public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) {
         data.setBrowserInfo(getRemoteId(request));
         return captchaService.check(data);

+ 5 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.controller.admin.sms;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum;
 import cn.iocoder.yudao.module.system.service.sms.SmsSendService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -24,6 +25,7 @@ public class SmsCallbackController {
 
     @PostMapping("/aliyun")
     @PermitAll
+    @TenantIgnore
     @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/document_detail/120998.html 文档")
     public CommonResult<Boolean> receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable {
         String text = ServletUtils.getBody(request);
@@ -33,6 +35,7 @@ public class SmsCallbackController {
 
     @PostMapping("/tencent")
     @PermitAll
+    @TenantIgnore
     @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/52077 文档")
     public CommonResult<Boolean> receiveTencentSmsStatus(HttpServletRequest request) throws Throwable {
         String text = ServletUtils.getBody(request);
@@ -43,6 +46,7 @@ public class SmsCallbackController {
 
     @PostMapping("/huawei")
     @PermitAll
+    @TenantIgnore
     @Operation(summary = "华为云短信的回调", description = "参见 https://support.huaweicloud.com/api-msgsms/sms_05_0003.html 文档")
     public CommonResult<Boolean> receiveHuaweiSmsStatus(@RequestBody String requestBody) throws Throwable {
         smsSendService.receiveSmsStatus(SmsChannelEnum.HUAWEI.getCode(), requestBody);
@@ -51,6 +55,7 @@ public class SmsCallbackController {
 
     @PostMapping("/qiniu")
     @PermitAll
+    @TenantIgnore
     @Operation(summary = "七牛云短信的回调", description = "参见 https://developer.qiniu.com/sms/5910/message-push 文档")
     public CommonResult<Boolean> receiveQiniuSmsStatus(@RequestBody String requestBody) throws Throwable {
         smsSendService.receiveSmsStatus(SmsChannelEnum.QINIU.getCode(), requestBody);

+ 4 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
@@ -39,6 +40,7 @@ public class TenantController {
 
     @GetMapping("/get-id-by-name")
     @PermitAll
+    @TenantIgnore
     @Operation(summary = "使用租户名,获得租户编号", description = "登录界面,根据用户的租户名,获得租户编号")
     @Parameter(name = "name", description = "租户名", required = true, example = "1024")
     public CommonResult<Long> getTenantIdByName(@RequestParam("name") String name) {
@@ -48,6 +50,7 @@ public class TenantController {
 
     @GetMapping({ "simple-list" })
     @PermitAll
+    @TenantIgnore
     @Operation(summary = "获取租户精简信息列表", description = "只包含被开启的租户,用于【首页】功能的选择租户选项")
     public CommonResult<List<TenantRespVO>> getTenantSimpleList() {
         List<TenantDO> list = tenantService.getTenantListByStatus(CommonStatusEnum.ENABLE.getStatus());
@@ -57,6 +60,7 @@ public class TenantController {
 
     @GetMapping("/get-by-website")
     @PermitAll
+    @TenantIgnore
     @Operation(summary = "使用域名,获得租户信息", description = "登录界面,根据用户的域名,获得租户信息")
     @Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn")
     public CommonResult<TenantRespVO> getTenantByWebsite(@RequestParam("website") String website) {

+ 0 - 9
yudao-server/src/main/resources/application.yaml

@@ -273,16 +273,7 @@ yudao:
   tenant: # 多租户相关配置项
     enable: true
     ignore-urls:
-      - /admin-api/system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号
-      - /admin-api/system/tenant/get-by-website # 基于域名获取租户,不许带租户编号
-      - /admin-api/system/tenant/simple-list # 获取租户列表,不许带租户编号
-      - /admin-api/system/captcha/get # 获取图片验证码,和租户无关
-      - /admin-api/system/captcha/check # 校验图片验证码,和租户无关
-      - /admin-api/infra/file/*/get/** # 获取图片,和租户无关
-      - /admin-api/system/sms/callback/* # 短信回调接口,无法带上租户编号
-      - /admin-api/pay/notify/** # 支付回调通知,不携带租户编号
       - /jmreport/* # 积木报表,无法携带租户编号
-      - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,无法携带租户编号
     ignore-tables:
       - system_tenant
       - system_tenant_package