# Conflicts:
#	yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java
#	yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
This commit is contained in:
YunaiV 2024-04-29 12:50:00 +08:00
commit b1c11f8dd8
10 changed files with 22 additions and 26 deletions

View File

@ -1,8 +1,6 @@
package cn.iocoder.yudao.framework.tenant.core.security; package cn.iocoder.yudao.framework.tenant.core.security;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.RpcConstants;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
@ -14,14 +12,13 @@ import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
import cn.iocoder.yudao.framework.web.config.WebProperties; import cn.iocoder.yudao.framework.web.config.WebProperties;
import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter;
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
@ -31,8 +28,6 @@ import java.util.Objects;
* 2. 如果请求未带租户的编号检查是否是忽略的 URL否则也不允许访问 * 2. 如果请求未带租户的编号检查是否是忽略的 URL否则也不允许访问
* 3. 校验租户是合法例如说被禁用到期 * 3. 校验租户是合法例如说被禁用到期
* *
* 校验用户访问的租户是否是其所在的租户
*
* @author 芋道源码 * @author 芋道源码
*/ */
@Slf4j @Slf4j
@ -56,17 +51,10 @@ public class TenantSecurityWebFilter extends ApiRequestFilter {
this.tenantFrameworkService = tenantFrameworkService; this.tenantFrameworkService = tenantFrameworkService;
} }
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return super.shouldNotFilter(request) &&
!StrUtil.startWithAny(request.getRequestURI(), RpcConstants.RPC_API_PREFIX); // 因为 RPC API 也会透传租户编号
}
@Override @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException { throws ServletException, IOException {
Long tenantId = TenantContextHolder.getTenantId(); Long tenantId = TenantContextHolder.getTenantId();
boolean isRpcRequest = WebFrameworkUtils.isRpcRequest(request);
// 1. 登陆的用户校验是否有权限访问该租户避免越权问题 // 1. 登陆的用户校验是否有权限访问该租户避免越权问题
LoginUser user = SecurityFrameworkUtils.getLoginUser(); LoginUser user = SecurityFrameworkUtils.getLoginUser();
if (user != null) { if (user != null) {
@ -75,8 +63,7 @@ public class TenantSecurityWebFilter extends ApiRequestFilter {
tenantId = user.getTenantId(); tenantId = user.getTenantId();
TenantContextHolder.setTenantId(tenantId); TenantContextHolder.setTenantId(tenantId);
// 如果传递了租户编号则进行比对租户编号避免越权问题 // 如果传递了租户编号则进行比对租户编号避免越权问题
} else if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId()) } else if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId())) {
&& !isRpcRequest) { // Cloud 特殊逻辑如果是 RPC 请求就不校验了主要考虑一些场景下会调用 TenantUtils 去切换租户
log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]", log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]",
user.getTenantId(), user.getId(), user.getUserType(), user.getTenantId(), user.getId(), user.getUserType(),
TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod()); TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod());

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.mybatis.config;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler; import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.extension.incrementer.*; import com.baomidou.mybatisplus.extension.incrementer.*;
@ -13,7 +14,6 @@ import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
/** /**
@ -21,7 +21,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
* *
* @author 芋道源码 * @author 芋道源码
*/ */
@AutoConfiguration @AutoConfiguration(before = MybatisPlusAutoConfiguration.class) // 目的先于 MyBatis Plus 自动配置避免 @MapperScan 可能扫描不到 Mapper 打印 warn 日志
@MapperScan(value = "${yudao.info.base-package}", annotationClass = Mapper.class, @MapperScan(value = "${yudao.info.base-package}", annotationClass = Mapper.class,
lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载目前仅用于单元测试 lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载目前仅用于单元测试
public class YudaoMybatisAutoConfiguration { public class YudaoMybatisAutoConfiguration {

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.redis.config;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.redisson.spring.starter.RedissonAutoConfigurationV2;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
@ -12,7 +13,7 @@ import org.springframework.data.redis.serializer.RedisSerializer;
/** /**
* Redis 配置类 * Redis 配置类
*/ */
@AutoConfiguration @AutoConfiguration(before = RedissonAutoConfigurationV2.class) // 目的使用自己定义的 RedisTemplate Bean
public class YudaoRedisAutoConfiguration { public class YudaoRedisAutoConfiguration {
/** /**

View File

@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi; import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@ -31,6 +32,7 @@ import javax.annotation.Resource;
* @author 芋道源码 * @author 芋道源码
*/ */
@AutoConfiguration @AutoConfiguration
@AutoConfigureOrder(-1) // 目的先于 Spring Security 自动配置避免一键改包后org.* 基础包无法生效
@EnableConfigurationProperties(SecurityProperties.class) @EnableConfigurationProperties(SecurityProperties.class)
public class YudaoSecurityAutoConfiguration { public class YudaoSecurityAutoConfiguration {

View File

@ -19,6 +19,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import java.util.HashMap; import java.util.HashMap;
@ -87,6 +88,7 @@ public class YudaoSwaggerAutoConfiguration {
* 自定义 OpenAPI 处理器 * 自定义 OpenAPI 处理器
*/ */
@Bean @Bean
@Primary // 目的以我们创建的 OpenAPIService Bean 为主避免一键改包后启动报错
public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI, public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,
SecurityService securityParser, SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties, SpringDocConfigProperties springDocConfigProperties,

View File

@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -122,7 +123,9 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
* @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder} * @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder}
*/ */
@Bean @Bean
@ConditionalOnMissingBean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build(); return restTemplateBuilder.build();
} }
} }

View File

@ -34,7 +34,8 @@ public class ProjectReactor {
* 白名单文件不进行重写避免出问题 * 白名单文件不进行重写避免出问题
*/ */
private static final Set<String> WHITE_FILE_TYPES = SetUtils.asSet("gif", "jpg", "svg", "png", // 图片 private static final Set<String> WHITE_FILE_TYPES = SetUtils.asSet("gif", "jpg", "svg", "png", // 图片
"eot", "woff2", "ttf", "woff"); // 字体 "eot", "woff2", "ttf", "woff", // 字体
"xdb"); // IP
public static void main(String[] args) { public static void main(String[] args) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();

View File

@ -87,7 +87,7 @@ public interface ErrorCodeConstants {
ErrorCode PRODUCT_CATEGORY_USED = new ErrorCode(1_020_009_002, "产品分类已关联产品"); ErrorCode PRODUCT_CATEGORY_USED = new ErrorCode(1_020_009_002, "产品分类已关联产品");
ErrorCode PRODUCT_CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_020_009_003, "父分类不存在"); ErrorCode PRODUCT_CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_020_009_003, "父分类不存在");
ErrorCode PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_020_009_004, "父分类不能是二级分类"); ErrorCode PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_020_009_004, "父分类不能是二级分类");
ErrorCode product_CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_020_009_005, "存在子分类,无法删除"); ErrorCode PRODUCT_CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_020_009_005, "存在子分类,无法删除");
// ========== 商机状态 1_020_010_000 ========== // ========== 商机状态 1_020_010_000 ==========
ErrorCode BUSINESS_STATUS_TYPE_NOT_EXISTS = new ErrorCode(1_020_010_000, "商机状态组不存在"); ErrorCode BUSINESS_STATUS_TYPE_NOT_EXISTS = new ErrorCode(1_020_010_000, "商机状态组不存在");

View File

@ -110,10 +110,10 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService
validateProductCategoryExists(id); validateProductCategoryExists(id);
// 1.2 校验是否还有子分类 // 1.2 校验是否还有子分类
if (productCategoryMapper.selectCountByParentId(id) > 0) { if (productCategoryMapper.selectCountByParentId(id) > 0) {
throw exception(product_CATEGORY_EXISTS_CHILDREN); throw exception(PRODUCT_CATEGORY_EXISTS_CHILDREN);
} }
// 1.3 校验是否被产品使用 // 1.3 校验是否被产品使用
if (crmProductService.getProductByCategoryId(id) !=null) { if (crmProductService.getProductByCategoryId(id) > 0) {
throw exception(PRODUCT_CATEGORY_USED); throw exception(PRODUCT_CATEGORY_USED);
} }
// 2. 删除 // 2. 删除

View File

@ -119,7 +119,7 @@ public class CouponServiceImpl implements CouponService {
Integer status = LocalDateTimeUtils.beforeNow(coupon.getValidEndTime()) Integer status = LocalDateTimeUtils.beforeNow(coupon.getValidEndTime())
? CouponStatusEnum.EXPIRE.getStatus() // 退还时可能已经过期了 ? CouponStatusEnum.EXPIRE.getStatus() // 退还时可能已经过期了
: CouponStatusEnum.UNUSED.getStatus(); : CouponStatusEnum.UNUSED.getStatus();
int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(), int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.USED.getStatus(),
new CouponDO().setStatus(status)); new CouponDO().setStatus(status));
if (updateCount == 0) { if (updateCount == 0) {
throw exception(COUPON_STATUS_NOT_USED); throw exception(COUPON_STATUS_NOT_USED);