mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-14 21:52:07 +08:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/oauth2/OAuth2TokenServiceImpl.java
This commit is contained in:
Vendored
+11
-3
@@ -17,13 +17,21 @@ import java.util.concurrent.Executors;
|
|||||||
public class CacheUtils {
|
public class CacheUtils {
|
||||||
|
|
||||||
public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {
|
public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {
|
||||||
Executor executor = Executors.newCachedThreadPool( // TODO 晨丰:可能要思考下,未来要不要做成可配置
|
|
||||||
TtlExecutors.getDefaultDisableInheritableThreadFactory()); // TTL 保证 ThreadLocal 可以透传
|
|
||||||
return CacheBuilder.newBuilder()
|
return CacheBuilder.newBuilder()
|
||||||
// 只阻塞当前数据加载线程,其他线程返回旧值
|
// 只阻塞当前数据加载线程,其他线程返回旧值
|
||||||
.refreshAfterWrite(duration)
|
.refreshAfterWrite(duration)
|
||||||
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
|
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
|
||||||
.build(CacheLoader.asyncReloading(loader, executor));
|
.build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建同步刷新的 LoadingCache 对象
|
||||||
|
*
|
||||||
|
* @param duration 过期时间
|
||||||
|
* @param loader CacheLoader 对象
|
||||||
|
* @return LoadingCache 对象
|
||||||
|
*/
|
||||||
|
public static <K, V> LoadingCache<K, V> buildCache(Duration duration, CacheLoader<K, V> loader) {
|
||||||
|
return CacheBuilder.newBuilder().refreshAfterWrite(duration).build(loader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -21,6 +21,11 @@ public class OrganContextHolder {
|
|||||||
*/
|
*/
|
||||||
private static final ThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>();
|
private static final ThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上下文获取不到organid异常提示
|
||||||
|
*/
|
||||||
|
public static final String ORGANID_NOT_EXIST_EXCEPTION = "OrganContextHolder 不存在组织编号!";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得组织编号
|
* 获得组织编号
|
||||||
*
|
*
|
||||||
@@ -48,8 +53,7 @@ public class OrganContextHolder {
|
|||||||
public static Long getRequiredOrganId() {
|
public static Long getRequiredOrganId() {
|
||||||
Long organId = getOrganId();
|
Long organId = getOrganId();
|
||||||
if (organId == null) {
|
if (organId == null) {
|
||||||
throw new NullPointerException("OrganContextHolder 不存在组织编号!可参考文档:"
|
throw new NullPointerException(ORGANID_NOT_EXIST_EXCEPTION);
|
||||||
+ DocumentEnum.ORGAN.getUrl());
|
|
||||||
}
|
}
|
||||||
return organId;
|
return organId;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-12
@@ -1,8 +1,6 @@
|
|||||||
package com.cf.imes.framework.organ.core.security;
|
package com.cf.imes.framework.organ.core.security;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import com.cf.imes.framework.common.enums.RpcConstants;
|
|
||||||
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
|
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||||
import com.cf.imes.framework.common.util.servlet.ServletUtils;
|
import com.cf.imes.framework.common.util.servlet.ServletUtils;
|
||||||
@@ -56,18 +54,10 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
|
|||||||
this.organFrameworkService = organFrameworkService;
|
this.organFrameworkService = organFrameworkService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 organId = WebFrameworkUtils.getOrganId(request);
|
Long organId = WebFrameworkUtils.getOrganId(request);
|
||||||
//Long organId = OrganContextHolder.getOrganId();
|
|
||||||
boolean isRpcRequest = WebFrameworkUtils.isRpcRequest(request);
|
|
||||||
// 1. 登陆的用户,校验是否有权限访问该组织,避免越权问题。
|
// 1. 登陆的用户,校验是否有权限访问该组织,避免越权问题。
|
||||||
LoginUser user = SecurityFrameworkUtils.getLoginUser();
|
LoginUser user = SecurityFrameworkUtils.getLoginUser();
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
@@ -76,8 +66,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
|
|||||||
organId = user.getOrganId();
|
organId = user.getOrganId();
|
||||||
OrganContextHolder.setOrganId(organId);
|
OrganContextHolder.setOrganId(organId);
|
||||||
// 如果传递了组织编号,则进行比对组织编号,避免越权问题
|
// 如果传递了组织编号,则进行比对组织编号,避免越权问题
|
||||||
} else if (!Objects.equals(user.getOrganId(), OrganContextHolder.getOrganId())
|
} else if (!Objects.equals(user.getOrganId(), OrganContextHolder.getOrganId())) { // Cloud 特殊逻辑:如果是 RPC 请求,就不校验了。主要考虑,一些场景下,会调用 OrganUtils 去切换组织
|
||||||
&& !isRpcRequest) { // Cloud 特殊逻辑:如果是 RPC 请求,就不校验了。主要考虑,一些场景下,会调用 OrganUtils 去切换组织
|
|
||||||
log.error("[doFilterInternal][组织({}) User({}/{}) 越权访问组织({}) URL({}/{})]",
|
log.error("[doFilterInternal][组织({}) User({}/{}) 越权访问组织({}) URL({}/{})]",
|
||||||
user.getOrganId(), user.getId(), user.getUserType(),
|
user.getOrganId(), user.getId(), user.getUserType(),
|
||||||
OrganContextHolder.getOrganId(), request.getRequestURI(), request.getMethod());
|
OrganContextHolder.getOrganId(), request.getRequestURI(), request.getMethod());
|
||||||
|
|||||||
+21
@@ -7,6 +7,7 @@ import com.cf.imes.framework.common.enums.DocumentEnum;
|
|||||||
import com.cf.imes.framework.mq.redis.core.RedisMQTemplate;
|
import com.cf.imes.framework.mq.redis.core.RedisMQTemplate;
|
||||||
import com.cf.imes.framework.mq.redis.core.job.RedisPendingMessageResendJob;
|
import com.cf.imes.framework.mq.redis.core.job.RedisPendingMessageResendJob;
|
||||||
import com.cf.imes.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener;
|
import com.cf.imes.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener;
|
||||||
|
import com.cf.imes.framework.mq.redis.core.pubsub.AbstractRedisSimpleMessageListener;
|
||||||
import com.cf.imes.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;
|
import com.cf.imes.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;
|
||||||
import com.cf.imes.framework.redis.config.ChenfengRedisAutoConfiguration;
|
import com.cf.imes.framework.redis.config.ChenfengRedisAutoConfiguration;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -61,6 +62,26 @@ public class ChenfengRedisMQConsumerAutoConfiguration {
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 Redis Pub/Sub 广播消费的容器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnBean(AbstractRedisSimpleMessageListener.class)
|
||||||
|
public RedisMessageListenerContainer redisDelListenerContainer(
|
||||||
|
RedisTemplate redisTemplate, List<AbstractRedisSimpleMessageListener> listeners) {
|
||||||
|
// 创建 RedisMessageListenerContainer 对象
|
||||||
|
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||||
|
// 设置 RedisConnection 工厂。
|
||||||
|
container.setConnectionFactory(redisTemplate.getRequiredConnectionFactory());
|
||||||
|
// 添加监听器
|
||||||
|
listeners.forEach(listener -> {
|
||||||
|
container.addMessageListener(listener, listener.getTopic());
|
||||||
|
log.info("[redisMessageListenerContainer][注册 ChannelTopic({}) 对应的监听器({})]",
|
||||||
|
listener.getTopic(), listener.getClass().getName());
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建 Redis Stream 重新消费的任务
|
* 创建 Redis Stream 重新消费的任务
|
||||||
*/
|
*/
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package com.cf.imes.framework.mq.redis.core.pubsub;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.springframework.data.redis.connection.MessageListener;
|
||||||
|
import org.springframework.data.redis.listener.ChannelTopic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redis监听器
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author 晨丰科技
|
||||||
|
*/
|
||||||
|
public abstract class AbstractRedisSimpleMessageListener implements MessageListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通道主题
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private ChannelTopic topic;
|
||||||
|
}
|
||||||
+11
-5
@@ -15,6 +15,8 @@ import com.cf.imes.module.system.api.oauth2.OAuth2TokenApi;
|
|||||||
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
||||||
import com.cf.imes.module.system.api.permission.PermissionApi;
|
import com.cf.imes.module.system.api.permission.PermissionApi;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
@@ -33,6 +35,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
* @author 晨丰科技
|
* @author 晨丰科技
|
||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final SecurityProperties securityProperties;
|
private final SecurityProperties securityProperties;
|
||||||
@@ -129,14 +132,17 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
.setOrganId(WebFrameworkUtils.getOrganId(request));
|
.setOrganId(WebFrameworkUtils.getOrganId(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
private LoginUser buildLoginUserByHeader(HttpServletRequest request) {
|
private LoginUser buildLoginUserByHeader(HttpServletRequest request) {
|
||||||
String loginUserStr = request.getHeader(SecurityFrameworkUtils.LOGIN_USER_HEADER);
|
String loginUserStr = request.getHeader(SecurityFrameworkUtils.LOGIN_USER_HEADER);
|
||||||
if(StrUtil.isNotEmpty(loginUserStr)) {
|
if(StrUtil.isNotEmpty(loginUserStr)) {
|
||||||
LoginUser loginUser = JsonUtils.parseObject(loginUserStr, LoginUser.class);
|
try {
|
||||||
//CommonResult<Boolean> superAdmin = permissionApi.hasAnyRoles(loginUser.getId(), "super_admin");
|
loginUserStr = URLDecoder.decode(loginUserStr, StandardCharsets.UTF_8.name()); // 解码,解决中文乱码问题
|
||||||
//loginUser.setIsSupAdmin(superAdmin.getCheckedData());
|
return JsonUtils.parseObject(loginUserStr, LoginUser.class);
|
||||||
loginUser.setNickname(URLDecoder.decode(loginUser.getNickname(),StandardCharsets.UTF_8));
|
} catch (Exception ex) {
|
||||||
return loginUser;
|
log.error("[buildLoginUserByHeader][解析 LoginUser({}) 发生异常]", loginUserStr, ex); ;
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-2
@@ -1,23 +1,37 @@
|
|||||||
package com.cf.imes.framework.security.core.rpc;
|
package com.cf.imes.framework.security.core.rpc;
|
||||||
|
|
||||||
import com.cf.imes.framework.rpc.core.util.FeignUtils;
|
import com.cf.imes.framework.common.util.json.JsonUtils;
|
||||||
import com.cf.imes.framework.security.core.LoginUser;
|
import com.cf.imes.framework.security.core.LoginUser;
|
||||||
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
|
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import feign.RequestInterceptor;
|
import feign.RequestInterceptor;
|
||||||
import feign.RequestTemplate;
|
import feign.RequestTemplate;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LoginUser 的 RequestInterceptor 实现类:Feign 请求时,将 {@link LoginUser} 设置到 header 中,继续透传给被调用的服务
|
* LoginUser 的 RequestInterceptor 实现类:Feign 请求时,将 {@link LoginUser} 设置到 header 中,继续透传给被调用的服务
|
||||||
*
|
*
|
||||||
* @author 晨丰科技
|
* @author 晨丰科技
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class LoginUserRequestInterceptor implements RequestInterceptor {
|
public class LoginUserRequestInterceptor implements RequestInterceptor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public void apply(RequestTemplate requestTemplate) {
|
public void apply(RequestTemplate requestTemplate) {
|
||||||
LoginUser user = SecurityFrameworkUtils.getLoginUser();
|
LoginUser user = SecurityFrameworkUtils.getLoginUser();
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
FeignUtils.createJsonHeader(requestTemplate, SecurityFrameworkUtils.LOGIN_USER_HEADER, user);
|
try {
|
||||||
|
String userStr = JsonUtils.toJsonString(user);
|
||||||
|
userStr = URLEncoder.encode(userStr, StandardCharsets.UTF_8.name()); // 编码,避免中文乱码
|
||||||
|
requestTemplate.header(SecurityFrameworkUtils.LOGIN_USER_HEADER, userStr);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("[apply][序列化 LoginUser({}) 发生异常]", user, ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
@@ -23,6 +23,13 @@ public interface SecurityFrameworkService {
|
|||||||
*/
|
*/
|
||||||
boolean hasAnyPermissions(String... permissions);
|
boolean hasAnyPermissions(String... permissions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空本地缓存
|
||||||
|
*
|
||||||
|
* @param
|
||||||
|
*/
|
||||||
|
void invalidateAll();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否有角色
|
* 判断是否有角色
|
||||||
*
|
*
|
||||||
|
|||||||
+7
-2
@@ -28,7 +28,7 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
|||||||
/**
|
/**
|
||||||
* 针对 {@link #hasAnyRoles(String...)} 的缓存
|
* 针对 {@link #hasAnyRoles(String...)} 的缓存
|
||||||
*/
|
*/
|
||||||
private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyRolesCache = CacheUtils.buildAsyncReloadingCache(
|
private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyRolesCache = CacheUtils.buildCache(
|
||||||
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
||||||
new CacheLoader<KeyValue<Long, List<String>>, Boolean>() {
|
new CacheLoader<KeyValue<Long, List<String>>, Boolean>() {
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
|||||||
/**
|
/**
|
||||||
* 针对 {@link #hasAnyPermissions(String...)} 的缓存
|
* 针对 {@link #hasAnyPermissions(String...)} 的缓存
|
||||||
*/
|
*/
|
||||||
private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyPermissionsCache = CacheUtils.buildAsyncReloadingCache(
|
private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyPermissionsCache = CacheUtils.buildCache(
|
||||||
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
||||||
new CacheLoader<KeyValue<Long, List<String>>, Boolean>() {
|
new CacheLoader<KeyValue<Long, List<String>>, Boolean>() {
|
||||||
|
|
||||||
@@ -65,6 +65,11 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
|||||||
return hasAnyPermissionsCache.get(new KeyValue<>(SecurityFrameworkUtils.getLoginUserId(), Arrays.asList(permissions)));
|
return hasAnyPermissionsCache.get(new KeyValue<>(SecurityFrameworkUtils.getLoginUserId(), Arrays.asList(permissions)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateAll() {
|
||||||
|
hasAnyPermissionsCache.invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasRole(String role) {
|
public boolean hasRole(String role) {
|
||||||
return hasAnyRoles(role);
|
return hasAnyRoles(role);
|
||||||
|
|||||||
+1
-21
@@ -22,11 +22,7 @@ import org.springframework.web.reactive.function.client.WebClient;
|
|||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@@ -74,14 +70,6 @@ public class TokenAuthenticationFilter implements GlobalFilter, Ordered {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
private final LoadingCache<String, Long> organIdCache = CacheUtils.buildAsyncReloadingCache(Duration.ofMinutes(1L), // 过期时间 1 分钟
|
|
||||||
new CacheLoader<>() {
|
|
||||||
@Override
|
|
||||||
public Long load(String token) {
|
|
||||||
return getOrganIdByToken(token).block();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
public TokenAuthenticationFilter(ReactorLoadBalancerExchangeFilterFunction lbFunction) {
|
public TokenAuthenticationFilter(ReactorLoadBalancerExchangeFilterFunction lbFunction) {
|
||||||
// Q:为什么不使用 OAuth2TokenApi 进行调用?
|
// Q:为什么不使用 OAuth2TokenApi 进行调用?
|
||||||
// A1:Spring Cloud OpenFeign 官方未内置 Reactive 的支持 https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#reactive-support
|
// A1:Spring Cloud OpenFeign 官方未内置 Reactive 的支持 https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#reactive-support
|
||||||
@@ -144,17 +132,9 @@ public class TokenAuthenticationFilter implements GlobalFilter, Ordered {
|
|||||||
private Mono<String> checkAccessToken(Long organId, String token) {
|
private Mono<String> checkAccessToken(Long organId, String token) {
|
||||||
return webClient.get()
|
return webClient.get()
|
||||||
.uri(OAuth2TokenApi.URL_CHECK, uriBuilder -> uriBuilder.queryParam("accessToken", token).build())
|
.uri(OAuth2TokenApi.URL_CHECK, uriBuilder -> uriBuilder.queryParam("accessToken", token).build())
|
||||||
// .headers(httpHeaders -> WebFrameworkUtils.setOrganIdHeader(organId, httpHeaders)) // 设置组织的 Header
|
|
||||||
.retrieve().bodyToMono(String.class);
|
.retrieve().bodyToMono(String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Long> getOrganIdByToken(String token) {
|
|
||||||
return webClient.get()
|
|
||||||
.uri(OAuth2TokenApi.URL_ORGAN, uriBuilder -> uriBuilder.queryParam("accessToken", token).build())
|
|
||||||
.retrieve()
|
|
||||||
.bodyToMono(Long.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LoginUser buildUser(String body) {
|
private LoginUser buildUser(String body) {
|
||||||
// 处理结果,结果不正确
|
// 处理结果,结果不正确
|
||||||
CommonResult<OAuth2AccessTokenCheckRespDTO> result = JsonUtils.parseObject(body, CHECK_RESULT_TYPE_REFERENCE);
|
CommonResult<OAuth2AccessTokenCheckRespDTO> result = JsonUtils.parseObject(body, CHECK_RESULT_TYPE_REFERENCE);
|
||||||
@@ -175,7 +155,7 @@ public class TokenAuthenticationFilter implements GlobalFilter, Ordered {
|
|||||||
.setOrganId(tokenInfo.getOrganId()).setScopes(tokenInfo.getScopes())
|
.setOrganId(tokenInfo.getOrganId()).setScopes(tokenInfo.getScopes())
|
||||||
.setLarge(tokenInfo.getLarge()).setDbNo(tokenInfo.getDbNo()).setTableNo(tokenInfo.getTableNo())
|
.setLarge(tokenInfo.getLarge()).setDbNo(tokenInfo.getDbNo()).setTableNo(tokenInfo.getTableNo())
|
||||||
.setDataCode(tokenInfo.getDataCode()).setIsSupAdmin(tokenInfo.getIsSupAdmin())
|
.setDataCode(tokenInfo.getDataCode()).setIsSupAdmin(tokenInfo.getIsSupAdmin())
|
||||||
.setNickname(URLEncoder.encode(tokenInfo.getNickname(), StandardCharsets.UTF_8));
|
.setNickname(tokenInfo.getNickname());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,10 +3,15 @@ package com.cf.imes.gateway.util;
|
|||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import com.cf.imes.framework.common.util.json.JsonUtils;
|
import com.cf.imes.framework.common.util.json.JsonUtils;
|
||||||
import com.cf.imes.gateway.filter.security.LoginUser;
|
import com.cf.imes.gateway.filter.security.LoginUser;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 安全服务工具类
|
* 安全服务工具类
|
||||||
*
|
*
|
||||||
@@ -14,6 +19,7 @@ import org.springframework.web.server.ServerWebExchange;
|
|||||||
*
|
*
|
||||||
* @author 晨丰科技
|
* @author 晨丰科技
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class SecurityFrameworkUtils {
|
public class SecurityFrameworkUtils {
|
||||||
|
|
||||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||||
@@ -101,8 +107,16 @@ public class SecurityFrameworkUtils {
|
|||||||
* @param builder 请求
|
* @param builder 请求
|
||||||
* @param user 用户
|
* @param user 用户
|
||||||
*/
|
*/
|
||||||
|
@SneakyThrows
|
||||||
public static void setLoginUserHeader(ServerHttpRequest.Builder builder, LoginUser user) {
|
public static void setLoginUserHeader(ServerHttpRequest.Builder builder, LoginUser user) {
|
||||||
builder.header(LOGIN_USER_HEADER, JsonUtils.toJsonString(user));
|
try {
|
||||||
|
String userStr = JsonUtils.toJsonString(user);
|
||||||
|
userStr = URLEncoder.encode(userStr, StandardCharsets.UTF_8.name()); // 编码,避免中文乱码
|
||||||
|
builder.header(LOGIN_USER_HEADER, userStr);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("[setLoginUserHeader][序列化 user({}) 发生异常]", user, ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
builder.header(DATA_CODE, user.getDataCode());
|
builder.header(DATA_CODE, user.getDataCode());
|
||||||
builder.header(ORGAN_ID, user.getOrganId().toString());
|
builder.header(ORGAN_ID, user.getOrganId().toString());
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -1,7 +1,7 @@
|
|||||||
package com.cf.imes.module.system.dal.mysql.permission;
|
package com.cf.imes.module.system.dal.mysql.permission;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.cf.imes.framework.common.pojo.PageResult;
|
import com.cf.imes.framework.common.pojo.PageResult;
|
||||||
import com.cf.imes.framework.mybatis.core.dataobject.BaseDO;
|
|
||||||
import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX;
|
import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import com.cf.imes.framework.security.core.LoginUser;
|
import com.cf.imes.framework.security.core.LoginUser;
|
||||||
@@ -12,9 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface RoleMapper extends BaseMapperX<RoleDO> {
|
public interface RoleMapper extends BaseMapperX<RoleDO> {
|
||||||
@@ -28,7 +26,7 @@ public interface RoleMapper extends BaseMapperX<RoleDO> {
|
|||||||
//如果是超级管理员并且未传organId,就查看自己的的
|
//如果是超级管理员并且未传organId,就查看自己的的
|
||||||
lambdaQueryWrapperX.inIfPresent(RoleDO::getOrganId,0L,loginUser.getOrganId());
|
lambdaQueryWrapperX.inIfPresent(RoleDO::getOrganId,0L,loginUser.getOrganId());
|
||||||
}*/
|
}*/
|
||||||
if(isSupAdmin && !Objects.isNull(reqVO.getOrganId())) {
|
if(isSupAdmin && ObjectUtil.isNotNull(reqVO.getOrganId())) {
|
||||||
//如果是超级管理员并且传入organId,就查看传入的组织
|
//如果是超级管理员并且传入organId,就查看传入的组织
|
||||||
lambdaQueryWrapperX.eqIfPresent(RoleDO::getOrganId, reqVO.getOrganId());
|
lambdaQueryWrapperX.eqIfPresent(RoleDO::getOrganId, reqVO.getOrganId());
|
||||||
} else {
|
} else {
|
||||||
@@ -41,7 +39,7 @@ public interface RoleMapper extends BaseMapperX<RoleDO> {
|
|||||||
.eqIfPresent(RoleDO::getStatus, reqVO.getStatus())
|
.eqIfPresent(RoleDO::getStatus, reqVO.getStatus())
|
||||||
.betweenIfPresent(RoleDO::getCreateTime, reqVO.getCreateTime())
|
.betweenIfPresent(RoleDO::getCreateTime, reqVO.getCreateTime())
|
||||||
.ne(RoleDO::getId, 1)
|
.ne(RoleDO::getId, 1)
|
||||||
.orderByDesc(RoleDO::getId);
|
.orderByAsc(RoleDO::getSort);
|
||||||
|
|
||||||
return selectPage(reqVO, lambdaQueryWrapperX);
|
return selectPage(reqVO, lambdaQueryWrapperX);
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
package com.cf.imes.module.system.dal.redis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* system redis监听刷新通道名称 常量
|
||||||
|
*
|
||||||
|
* @author Gqr
|
||||||
|
* @since 2024/6/13 14:55
|
||||||
|
*/
|
||||||
|
public interface RedisRefreshChannelTopicConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新权限
|
||||||
|
*/
|
||||||
|
String PREMISSION_REFRESH = "PREMISSION_REFRESH";
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package com.cf.imes.module.system.dal.redis.listener;
|
||||||
|
|
||||||
|
import com.cf.imes.framework.mq.redis.core.pubsub.AbstractRedisSimpleMessageListener;
|
||||||
|
import com.cf.imes.framework.security.core.service.SecurityFrameworkService;
|
||||||
|
import com.cf.imes.module.system.dal.redis.RedisRefreshChannelTopicConstants;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.connection.Message;
|
||||||
|
import org.springframework.data.redis.listener.ChannelTopic;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* system PREMISSION_REFRESH redis监听器
|
||||||
|
*
|
||||||
|
* @author Gqr
|
||||||
|
* @since 2024/6/13 10:32
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SystemPremissionRefreshRedisListener extends AbstractRedisSimpleMessageListener {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SecurityFrameworkService securityFrameworkService;
|
||||||
|
|
||||||
|
public SystemPremissionRefreshRedisListener() {
|
||||||
|
super.setTopic(new ChannelTopic(RedisRefreshChannelTopicConstants.PREMISSION_REFRESH));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(Message message, byte[] bytes) {
|
||||||
|
securityFrameworkService.invalidateAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-2
@@ -4,14 +4,12 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
||||||
import com.cf.imes.framework.common.util.collection.CollectionUtils;
|
import com.cf.imes.framework.common.util.collection.CollectionUtils;
|
||||||
import com.cf.imes.framework.datapermission.core.annotation.DataPermission;
|
import com.cf.imes.framework.datapermission.core.annotation.DataPermission;
|
||||||
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import com.cf.imes.framework.organ.core.aop.OrganIgnore;
|
import com.cf.imes.framework.organ.core.aop.OrganIgnore;
|
||||||
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
|
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
|
||||||
import com.cf.imes.framework.organ.core.db.OrganBaseDO;
|
|
||||||
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
|
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import com.cf.imes.module.system.api.permission.dto.DeptDataPermissionRespDTO;
|
import com.cf.imes.module.system.api.permission.dto.DeptDataPermissionRespDTO;
|
||||||
import com.cf.imes.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO;
|
import com.cf.imes.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO;
|
||||||
@@ -22,6 +20,7 @@ import com.cf.imes.module.system.dal.dataobject.permission.UserRoleDO;
|
|||||||
import com.cf.imes.module.system.dal.mysql.permission.RoleMenuMapper;
|
import com.cf.imes.module.system.dal.mysql.permission.RoleMenuMapper;
|
||||||
import com.cf.imes.module.system.dal.mysql.permission.UserRoleMapper;
|
import com.cf.imes.module.system.dal.mysql.permission.UserRoleMapper;
|
||||||
import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
|
import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
|
||||||
|
import com.cf.imes.module.system.dal.redis.RedisRefreshChannelTopicConstants;
|
||||||
import com.cf.imes.module.system.enums.permission.DataScopeEnum;
|
import com.cf.imes.module.system.enums.permission.DataScopeEnum;
|
||||||
import com.cf.imes.module.system.service.dept.DeptService;
|
import com.cf.imes.module.system.service.dept.DeptService;
|
||||||
import com.cf.imes.module.system.service.user.AdminUserService;
|
import com.cf.imes.module.system.service.user.AdminUserService;
|
||||||
@@ -30,14 +29,17 @@ import com.google.common.annotations.VisibleForTesting;
|
|||||||
import com.google.common.base.Suppliers;
|
import com.google.common.base.Suppliers;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.cache.annotation.Caching;
|
import org.springframework.cache.annotation.Caching;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -72,6 +74,9 @@ public class PermissionServiceImpl implements PermissionService {
|
|||||||
@Resource
|
@Resource
|
||||||
private AdminUserService userService;
|
private AdminUserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisTemplate redisTemplate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasAnyPermissions(Long userId, String... permissions) {
|
public boolean hasAnyPermissions(Long userId, String... permissions) {
|
||||||
// 如果为空,说明已经有权限
|
// 如果为空,说明已经有权限
|
||||||
@@ -179,6 +184,16 @@ public class PermissionServiceImpl implements PermissionService {
|
|||||||
if (CollUtil.isNotEmpty(deleteMenuIds)) {
|
if (CollUtil.isNotEmpty(deleteMenuIds)) {
|
||||||
roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
|
roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 角色菜单权限发生修改,通知刷新本地缓存
|
||||||
|
if (CollectionUtil.isNotEmpty(createMenuIds) || CollectionUtil.isNotEmpty(deleteMenuIds)) {
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
redisTemplate.convertAndSend(RedisRefreshChannelTopicConstants.PREMISSION_REFRESH, "");
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
log.error("redis message发送失败, topic:{}, 异常:{}", RedisRefreshChannelTopicConstants.PREMISSION_REFRESH, e.getMessage());
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user