mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-12 21:02:08 +08:00
新增组织产品有效性的检查能力
This commit is contained in:
+1
@@ -25,6 +25,7 @@ public class GlobalErrorCodeConstants {
|
||||
public static final ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
|
||||
public static final ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
|
||||
public static final ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试");
|
||||
public static final ErrorCode ORG_PRODUCT_EXPIRED = new ErrorCode(410, "组织产品套餐已过期,请续费后继续使用");
|
||||
|
||||
// ========== 服务端错误段 ==========
|
||||
|
||||
|
||||
+5
@@ -32,6 +32,11 @@ public class OrganProperties {
|
||||
*/
|
||||
private Set<String> ignoreUrls = Collections.emptySet();
|
||||
|
||||
/**
|
||||
* 需要忽略产品检查的请求
|
||||
*/
|
||||
private Set<String> ignoreProductUrls = Collections.emptySet();
|
||||
|
||||
/**
|
||||
* 需要忽略多组织的表
|
||||
*
|
||||
|
||||
+35
-2
@@ -2,6 +2,7 @@ package com.cf.imes.framework.organ.core.security;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.cf.imes.framework.common.enums.UserTypeEnum;
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
@@ -23,6 +24,7 @@ import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -31,7 +33,7 @@ import java.util.Objects;
|
||||
* 1. 如果是登陆的用户,校验是否有权限访问该组织,避免越权问题。
|
||||
* 2. 如果请求未带组织的编号,检查是否是忽略的 URL,否则也不允许访问。
|
||||
* 3. 校验组织是合法,例如说被禁用、到期
|
||||
*
|
||||
* <p>
|
||||
* 校验用户访问的组织,是否是其所在的组织,
|
||||
*
|
||||
* @author 晨丰科技
|
||||
@@ -70,7 +72,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
|
||||
if (organId == null) {
|
||||
organId = user.getOrganId();
|
||||
OrganContextHolder.setOrganId(organId);
|
||||
// 如果传递了组织编号,则进行比对组织编号,避免越权问题
|
||||
// 如果传递了组织编号,则进行比对组织编号,避免越权问题
|
||||
} else if (!Objects.equals(user.getOrganId(), OrganContextHolder.getOrganId())) { // Cloud 特殊逻辑:如果是 RPC 请求,就不校验了。主要考虑,一些场景下,会调用 OrganUtils 去切换组织
|
||||
log.error("[doFilterInternal][组织({}) User({}/{}) 越权访问组织({}) URL({}/{})]",
|
||||
user.getOrganId(), user.getId(), user.getUserType(),
|
||||
@@ -98,6 +100,10 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
|
||||
// 校验部门
|
||||
organFrameworkService.validDept(deptId);
|
||||
}
|
||||
|
||||
// 校验组织产品有效性
|
||||
validOrgnProduct(user, request);
|
||||
|
||||
} catch (Throwable ex) {
|
||||
CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);
|
||||
// 组织失效返回401踢出系统
|
||||
@@ -115,6 +121,19 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验组织产品有效性
|
||||
*
|
||||
* @param user
|
||||
* @param request
|
||||
*/
|
||||
private void validOrgnProduct(LoginUser user, HttpServletRequest request) {
|
||||
// 非管理端校验产品的有效性
|
||||
if (!Objects.equals(user.getUserType(), UserTypeEnum.ADMIN.getValue()) && !isProductIgnoreUrl(request)) {
|
||||
organFrameworkService.validOrgProduct(user.getOrganId(), user.getProductId());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isIgnoreUrl(HttpServletRequest request) {
|
||||
// 快速匹配,保证性能
|
||||
if (CollUtil.contains(organProperties.getIgnoreUrls(), request.getRequestURI())) {
|
||||
@@ -129,6 +148,20 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isProductIgnoreUrl(HttpServletRequest request) {
|
||||
// 快速匹配,保证性能
|
||||
if (CollUtil.contains(organProperties.getIgnoreProductUrls(), request.getRequestURI())) {
|
||||
return true;
|
||||
}
|
||||
// 逐个 Ant 路径匹配
|
||||
for (String url : organProperties.getIgnoreProductUrls()) {
|
||||
if (pathMatcher.match(url, request.getRequestURI())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否组织失效设置未登录回调
|
||||
*
|
||||
|
||||
+8
@@ -29,4 +29,12 @@ public interface OrganFrameworkService {
|
||||
* @param id
|
||||
*/
|
||||
void validDept(Long id);
|
||||
|
||||
/**
|
||||
* 校验组织产品是否有效
|
||||
*
|
||||
* @param organId
|
||||
* @param productId
|
||||
*/
|
||||
void validOrgProduct(Long organId, Long productId);
|
||||
}
|
||||
|
||||
+21
@@ -1,5 +1,6 @@
|
||||
package com.cf.imes.framework.organ.core.service;
|
||||
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
import com.cf.imes.framework.common.util.cache.CacheUtils;
|
||||
import com.cf.imes.module.system.api.dept.DeptApi;
|
||||
@@ -66,6 +67,20 @@ public class OrganFrameworkServiceImpl implements OrganFrameworkService {
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* 针对 {@link #validOrgProduct(Long, Long)} 的缓存
|
||||
*/
|
||||
private final LoadingCache<Pair<Long, Long>, CommonResult<Boolean>> validOrgProduct = CacheUtils.buildCache(
|
||||
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
||||
new CacheLoader<>() {
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> load(Pair<Long, Long> pair) {
|
||||
return organApi.validOrganProduct(pair.getKey(), pair.getValue());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public List<Long> getOrganIds() {
|
||||
@@ -83,4 +98,10 @@ public class OrganFrameworkServiceImpl implements OrganFrameworkService {
|
||||
public void validDept(Long id) {
|
||||
validOrganDept.get(id).checkError();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void validOrgProduct(Long organId, Long productId) {
|
||||
validOrgProduct.get(new Pair<>(organId, productId)).checkError();
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -85,4 +85,9 @@ public class LoginUser {
|
||||
* 用户昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 产品id
|
||||
*/
|
||||
private Long productId;
|
||||
}
|
||||
|
||||
+2
-1
@@ -101,7 +101,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||
// 构建登录用户
|
||||
return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
|
||||
.setOrganId(accessToken.getOrganId()).setScopes(accessToken.getScopes()).setIsSupAdmin(accessToken.getIsSupAdmin())
|
||||
.setLarge(accessToken.getLarge()).setDbNo(accessToken.getDbNo()).setTableNo(accessToken.getTableNo());
|
||||
.setLarge(accessToken.getLarge()).setDbNo(accessToken.getDbNo()).setTableNo(accessToken.getTableNo())
|
||||
.setProductId(accessToken.getProductId());
|
||||
} catch (ServiceException serviceException) {
|
||||
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
|
||||
return null;
|
||||
|
||||
+13
@@ -178,4 +178,17 @@ public class SecurityFrameworkUtils {
|
||||
Long organId = (organIds != null && organIds.length > 0) ? organIds[0] : getUserOrganId();
|
||||
return Objects.equals(organId, CF_ORGANID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户的productId
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Long getProductId() {
|
||||
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
throw new ServiceException(GlobalErrorCodeConstants.UNAUTHORIZED);
|
||||
}
|
||||
return loginUser.getProductId();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user