Commit be86cdfd517d65fcaf513f1f6b6daf11d5f12533

Authored by YunaiV
1 parent 5a87b33d

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

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 3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
5 5 import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties;
  6 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
6 7 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;
7 8 import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
8 9 import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect;
... ... @@ -19,11 +20,13 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
19 20 import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
20 21 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
21 22 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
  23 +import jakarta.annotation.Resource;
22 24 import org.springframework.boot.autoconfigure.AutoConfiguration;
23 25 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
24 26 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
25 27 import org.springframework.boot.context.properties.EnableConfigurationProperties;
26 28 import org.springframework.boot.web.servlet.FilterRegistrationBean;
  29 +import org.springframework.context.ApplicationContext;
27 30 import org.springframework.context.annotation.Bean;
28 31 import org.springframework.context.annotation.Primary;
29 32 import org.springframework.data.redis.cache.BatchStrategies;
... ... @@ -32,14 +35,24 @@ import org.springframework.data.redis.cache.RedisCacheManager;
32 35 import org.springframework.data.redis.cache.RedisCacheWriter;
33 36 import org.springframework.data.redis.connection.RedisConnectionFactory;
34 37 import org.springframework.data.redis.core.RedisTemplate;
  38 +import org.springframework.web.method.HandlerMethod;
  39 +import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
  40 +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  41 +import org.springframework.web.util.pattern.PathPattern;
35 42  
  43 +import java.util.Map;
36 44 import java.util.Objects;
37 45  
  46 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  47 +
38 48 @AutoConfiguration
39 49 @ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户
40 50 @EnableConfigurationProperties(TenantProperties.class)
41 51 public class YudaoTenantAutoConfiguration {
42 52  
  53 + @Resource
  54 + private ApplicationContext applicationContext;
  55 +
43 56 @Bean
44 57 public TenantFrameworkService tenantFrameworkService(TenantApi tenantApi) {
45 58 return new TenantFrameworkServiceImpl(tenantApi);
... ... @@ -67,13 +80,41 @@ public class YudaoTenantAutoConfiguration {
67 80 // ========== WEB ==========
68 81  
69 82 @Bean
70   - public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() {
  83 + public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter(TenantProperties tenantProperties) {
71 84 FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>();
72 85 registrationBean.setFilter(new TenantContextWebFilter());
73 86 registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER);
  87 + addIgnoreUrls(tenantProperties);
74 88 return registrationBean;
75 89 }
76 90  
  91 + /**
  92 + * 如果 Controller 接口上,有 {@link TenantIgnore} 注解,那么添加到忽略的 URL 中
  93 + *
  94 + * @param tenantProperties 租户配置
  95 + */
  96 + private void addIgnoreUrls(TenantProperties tenantProperties) {
  97 + // 获得接口对应的 HandlerMethod 集合
  98 + RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
  99 + applicationContext.getBean("requestMappingHandlerMapping");
  100 + Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
  101 + // 获得有 @TenantIgnore 注解的接口
  102 + for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
  103 + HandlerMethod handlerMethod = entry.getValue();
  104 + if (!handlerMethod.hasMethodAnnotation(TenantIgnore.class)) {
  105 + continue;
  106 + }
  107 + // 添加到忽略的 URL 中
  108 + if (entry.getKey().getPatternsCondition() != null) {
  109 + tenantProperties.getIgnoreUrls().addAll(entry.getKey().getPatternsCondition().getPatterns());
  110 + }
  111 + if (entry.getKey().getPathPatternsCondition() != null) {
  112 + tenantProperties.getIgnoreUrls().addAll(
  113 + convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));
  114 + }
  115 + }
  116 + }
  117 +
77 118 // ========== Security ==========
78 119  
79 120 @Bean
... ...
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;
6 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
9 10 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*;
10 11 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
11 12 import cn.iocoder.yudao.module.infra.service.file.FileService;
... ... @@ -76,6 +77,7 @@ public class FileController {
76 77  
77 78 @GetMapping("/{configId}/get/**")
78 79 @PermitAll
  80 + @TenantIgnore
79 81 @Operation(summary = "下载文件")
80 82 @Parameter(name = "configId", description = "配置编号", required = true)
81 83 public void getFileContent(HttpServletRequest request,
... ...
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;
2 2  
3 3 import cn.hutool.core.lang.Assert;
4 4 import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
5 6 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
6 7 import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenCheckSignatureReqVO;
7 8 import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenHandleMessageReqVO;
... ... @@ -36,32 +37,13 @@ public class MpOpenController {
36 37 private MpAccountService mpAccountService;
37 38  
38 39 /**
39   - * 接收微信公众号的校验签名
40   - *
41   - * 对应 <a href="https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html">文档</a>
42   - */
43   - @Operation(summary = "校验签名") // 参见
44   - @GetMapping(value = "/{appId}", produces = "text/plain;charset=utf-8")
45   - public String checkSignature(@PathVariable("appId") String appId,
46   - MpOpenCheckSignatureReqVO reqVO) {
47   - log.info("[checkSignature][appId({}) 接收到来自微信服务器的认证消息({})]", appId, reqVO);
48   - // 校验请求签名
49   - WxMpService wxMpService = mpServiceFactory.getRequiredMpService(appId);
50   - // 校验通过
51   - if (wxMpService.checkSignature(reqVO.getTimestamp(), reqVO.getNonce(), reqVO.getSignature())) {
52   - return reqVO.getEchostr();
53   - }
54   - // 校验不通过
55   - return "非法请求";
56   - }
57   -
58   - /**
59 40 * 接收微信公众号的消息推送
60 41 *
61 42 * <a href="https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html">文档</a>
62 43 */
63 44 @Operation(summary = "处理消息")
64 45 @PostMapping(value = "/{appId}", produces = "application/xml; charset=UTF-8")
  46 + @TenantIgnore
65 47 public String handleMessage(@PathVariable("appId") String appId,
66 48 @RequestBody String content,
67 49 MpOpenHandleMessageReqVO reqVO) {
... ... @@ -79,6 +61,27 @@ public class MpOpenController {
79 61 }
80 62 }
81 63  
  64 + /**
  65 + * 接收微信公众号的校验签名
  66 + *
  67 + * 对应 <a href="https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html">文档</a>
  68 + */
  69 + @Operation(summary = "校验签名") // 参见
  70 + @GetMapping(value = "/{appId}", produces = "text/plain;charset=utf-8")
  71 + @TenantIgnore
  72 + public String checkSignature(@PathVariable("appId") String appId,
  73 + MpOpenCheckSignatureReqVO reqVO) {
  74 + log.info("[checkSignature][appId({}) 接收到来自微信服务器的认证消息({})]", appId, reqVO);
  75 + // 校验请求签名
  76 + WxMpService wxMpService = mpServiceFactory.getRequiredMpService(appId);
  77 + // 校验通过
  78 + if (wxMpService.checkSignature(reqVO.getTimestamp(), reqVO.getNonce(), reqVO.getSignature())) {
  79 + return reqVO.getEchostr();
  80 + }
  81 + // 校验不通过
  82 + return "非法请求";
  83 + }
  84 +
82 85 private String handleMessage0(String appId, String content, MpOpenHandleMessageReqVO reqVO) {
83 86 // 校验请求签名
84 87 WxMpService mppService = mpServiceFactory.getRequiredMpService(appId);
... ...
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;
7 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
8 8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
9 9 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  10 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
10 11 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
11 12 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
12 13 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO;
... ... @@ -62,6 +63,7 @@ public class PayNotifyController {
62 63 @PostMapping(value = "/order/{channelId}")
63 64 @Operation(summary = "支付渠道的统一【支付】回调")
64 65 @PermitAll
  66 + @TenantIgnore
65 67 public String notifyOrder(@PathVariable("channelId") Long channelId,
66 68 @RequestParam(required = false) Map<String, String> params,
67 69 @RequestBody(required = false) String body) {
... ... @@ -82,6 +84,7 @@ public class PayNotifyController {
82 84 @PostMapping(value = "/refund/{channelId}")
83 85 @Operation(summary = "支付渠道的统一【退款】回调")
84 86 @PermitAll
  87 + @TenantIgnore
85 88 public String notifyRefund(@PathVariable("channelId") Long channelId,
86 89 @RequestParam(required = false) Map<String, String> params,
87 90 @RequestBody(required = false) String body) {
... ... @@ -102,6 +105,7 @@ public class PayNotifyController {
102 105 @PostMapping(value = "/transfer/{channelId}")
103 106 @Operation(summary = "支付渠道的统一【转账】回调")
104 107 @PermitAll
  108 + @TenantIgnore
105 109 public String notifyTransfer(@PathVariable("channelId") Long channelId,
106 110 @RequestParam(required = false) Map<String, String> params,
107 111 @RequestBody(required = false) String body) {
... ...
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;
2 2  
3 3 import cn.hutool.core.util.StrUtil;
4 4 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
  5 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
5 6 import com.anji.captcha.model.common.ResponseModel;
6 7 import com.anji.captcha.model.vo.CaptchaVO;
7 8 import com.anji.captcha.service.CaptchaService;
... ... @@ -26,6 +27,7 @@ public class CaptchaController {
26 27 @PostMapping({"/get"})
27 28 @Operation(summary = "获得验证码")
28 29 @PermitAll
  30 + @TenantIgnore
29 31 public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) {
30 32 assert request.getRemoteHost() != null;
31 33 data.setBrowserInfo(getRemoteId(request));
... ... @@ -35,6 +37,7 @@ public class CaptchaController {
35 37 @PostMapping("/check")
36 38 @Operation(summary = "校验验证码")
37 39 @PermitAll
  40 + @TenantIgnore
38 41 public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) {
39 42 data.setBrowserInfo(getRemoteId(request));
40 43 return captchaService.check(data);
... ...
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;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
  5 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
5 6 import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum;
6 7 import cn.iocoder.yudao.module.system.service.sms.SmsSendService;
7 8 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -24,6 +25,7 @@ public class SmsCallbackController {
24 25  
25 26 @PostMapping("/aliyun")
26 27 @PermitAll
  28 + @TenantIgnore
27 29 @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/document_detail/120998.html 文档")
28 30 public CommonResult<Boolean> receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable {
29 31 String text = ServletUtils.getBody(request);
... ... @@ -33,6 +35,7 @@ public class SmsCallbackController {
33 35  
34 36 @PostMapping("/tencent")
35 37 @PermitAll
  38 + @TenantIgnore
36 39 @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/52077 文档")
37 40 public CommonResult<Boolean> receiveTencentSmsStatus(HttpServletRequest request) throws Throwable {
38 41 String text = ServletUtils.getBody(request);
... ... @@ -43,6 +46,7 @@ public class SmsCallbackController {
43 46  
44 47 @PostMapping("/huawei")
45 48 @PermitAll
  49 + @TenantIgnore
46 50 @Operation(summary = "华为云短信的回调", description = "参见 https://support.huaweicloud.com/api-msgsms/sms_05_0003.html 文档")
47 51 public CommonResult<Boolean> receiveHuaweiSmsStatus(@RequestBody String requestBody) throws Throwable {
48 52 smsSendService.receiveSmsStatus(SmsChannelEnum.HUAWEI.getCode(), requestBody);
... ... @@ -51,6 +55,7 @@ public class SmsCallbackController {
51 55  
52 56 @PostMapping("/qiniu")
53 57 @PermitAll
  58 + @TenantIgnore
54 59 @Operation(summary = "七牛云短信的回调", description = "参见 https://developer.qiniu.com/sms/5910/message-push 文档")
55 60 public CommonResult<Boolean> receiveQiniuSmsStatus(@RequestBody String requestBody) throws Throwable {
56 61 smsSendService.receiveSmsStatus(SmsChannelEnum.QINIU.getCode(), requestBody);
... ...
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;
7 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  10 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
10 11 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
11 12 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
12 13 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
... ... @@ -39,6 +40,7 @@ public class TenantController {
39 40  
40 41 @GetMapping("/get-id-by-name")
41 42 @PermitAll
  43 + @TenantIgnore
42 44 @Operation(summary = "使用租户名,获得租户编号", description = "登录界面,根据用户的租户名,获得租户编号")
43 45 @Parameter(name = "name", description = "租户名", required = true, example = "1024")
44 46 public CommonResult<Long> getTenantIdByName(@RequestParam("name") String name) {
... ... @@ -48,6 +50,7 @@ public class TenantController {
48 50  
49 51 @GetMapping({ "simple-list" })
50 52 @PermitAll
  53 + @TenantIgnore
51 54 @Operation(summary = "获取租户精简信息列表", description = "只包含被开启的租户,用于【首页】功能的选择租户选项")
52 55 public CommonResult<List<TenantRespVO>> getTenantSimpleList() {
53 56 List<TenantDO> list = tenantService.getTenantListByStatus(CommonStatusEnum.ENABLE.getStatus());
... ... @@ -57,6 +60,7 @@ public class TenantController {
57 60  
58 61 @GetMapping("/get-by-website")
59 62 @PermitAll
  63 + @TenantIgnore
60 64 @Operation(summary = "使用域名,获得租户信息", description = "登录界面,根据用户的域名,获得租户信息")
61 65 @Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn")
62 66 public CommonResult<TenantRespVO> getTenantByWebsite(@RequestParam("website") String website) {
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -273,16 +273,7 @@ yudao:
273 273 tenant: # 多租户相关配置项
274 274 enable: true
275 275 ignore-urls:
276   - - /admin-api/system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号
277   - - /admin-api/system/tenant/get-by-website # 基于域名获取租户,不许带租户编号
278   - - /admin-api/system/tenant/simple-list # 获取租户列表,不许带租户编号
279   - - /admin-api/system/captcha/get # 获取图片验证码,和租户无关
280   - - /admin-api/system/captcha/check # 校验图片验证码,和租户无关
281   - - /admin-api/infra/file/*/get/** # 获取图片,和租户无关
282   - - /admin-api/system/sms/callback/* # 短信回调接口,无法带上租户编号
283   - - /admin-api/pay/notify/** # 支付回调通知,不携带租户编号
284 276 - /jmreport/* # 积木报表,无法携带租户编号
285   - - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,无法携带租户编号
286 277 ignore-tables:
287 278 - system_tenant
288 279 - system_tenant_package
... ...