mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2025-09-24 07:19:46 +08:00
发布 v2.3.0
This commit is contained in:
@ -24,167 +24,147 @@ import java.util.Map;
|
||||
/**
|
||||
* 数据过滤处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class DataScopeAspect
|
||||
{
|
||||
/**
|
||||
* 全部数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_ALL = "1";
|
||||
public class DataScopeAspect {
|
||||
|
||||
/**
|
||||
* 自定数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_CUSTOM = "2";
|
||||
/**
|
||||
* 全部数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_ALL = "1";
|
||||
|
||||
/**
|
||||
* 部门数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT = "3";
|
||||
/**
|
||||
* 自定数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_CUSTOM = "2";
|
||||
|
||||
/**
|
||||
* 部门及以下数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
|
||||
/**
|
||||
* 部门数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT = "3";
|
||||
|
||||
/**
|
||||
* 仅本人数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_SELF = "5";
|
||||
/**
|
||||
* 部门及以下数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
|
||||
|
||||
/**
|
||||
* 数据权限过滤关键字
|
||||
*/
|
||||
public static final String DATA_SCOPE = "dataScope";
|
||||
/**
|
||||
* 仅本人数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_SELF = "5";
|
||||
|
||||
// 配置织入点
|
||||
@Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")
|
||||
public void dataScopePointCut()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* 数据权限过滤关键字
|
||||
*/
|
||||
public static final String DATA_SCOPE = "dataScope";
|
||||
|
||||
@Before("dataScopePointCut()")
|
||||
public void doBefore(JoinPoint point) throws Throwable
|
||||
{
|
||||
// 配置织入点
|
||||
@Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")
|
||||
public void dataScopePointCut() {
|
||||
}
|
||||
|
||||
@Before("dataScopePointCut()")
|
||||
public void doBefore(JoinPoint point) throws Throwable {
|
||||
clearDataScope(point);
|
||||
handleDataScope(point);
|
||||
}
|
||||
handleDataScope(point);
|
||||
}
|
||||
|
||||
protected void handleDataScope(final JoinPoint joinPoint)
|
||||
{
|
||||
// 获得注解
|
||||
DataScope controllerDataScope = getAnnotationLog(joinPoint);
|
||||
if (controllerDataScope == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 获取当前的用户
|
||||
LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
|
||||
if (Validator.isNotNull(loginUser))
|
||||
{
|
||||
SysUser currentUser = loginUser.getUser();
|
||||
// 如果是超级管理员,则不过滤数据
|
||||
if (Validator.isNotNull(currentUser) && !currentUser.isAdmin())
|
||||
{
|
||||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
|
||||
controllerDataScope.userAlias());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据范围过滤
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @param user 用户
|
||||
* @param userAlias 别名
|
||||
*/
|
||||
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
|
||||
{
|
||||
StringBuilder sqlString = new StringBuilder();
|
||||
|
||||
for (SysRole role : user.getRoles())
|
||||
{
|
||||
String dataScope = role.getDataScope();
|
||||
if (DATA_SCOPE_ALL.equals(dataScope))
|
||||
{
|
||||
sqlString = new StringBuilder();
|
||||
break;
|
||||
}
|
||||
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
|
||||
{
|
||||
sqlString.append(StrUtil.format(
|
||||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
|
||||
role.getRoleId()));
|
||||
}
|
||||
else if (DATA_SCOPE_DEPT.equals(dataScope))
|
||||
{
|
||||
sqlString.append(StrUtil.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
|
||||
}
|
||||
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
|
||||
{
|
||||
sqlString.append(StrUtil.format(
|
||||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
|
||||
deptAlias, user.getDeptId(), user.getDeptId()));
|
||||
}
|
||||
else if (DATA_SCOPE_SELF.equals(dataScope))
|
||||
{
|
||||
if (StrUtil.isNotBlank(userAlias))
|
||||
{
|
||||
sqlString.append(StrUtil.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 数据权限为仅本人且没有userAlias别名不查询任何数据
|
||||
sqlString.append(" OR 1=0 ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(sqlString.toString()))
|
||||
{
|
||||
putDataScope(joinPoint, "AND (" + sqlString.substring(4) + ")");
|
||||
protected void handleDataScope(final JoinPoint joinPoint) {
|
||||
// 获得注解
|
||||
DataScope controllerDataScope = getAnnotationLog(joinPoint);
|
||||
if (controllerDataScope == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 获取当前的用户
|
||||
LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
|
||||
if (Validator.isNotNull(loginUser)) {
|
||||
SysUser currentUser = loginUser.getUser();
|
||||
// 如果是超级管理员,则不过滤数据
|
||||
if (Validator.isNotNull(currentUser) && !currentUser.isAdmin()) {
|
||||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
|
||||
controllerDataScope.userAlias(), controllerDataScope.isUser());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在注解,如果存在就获取
|
||||
*/
|
||||
private DataScope getAnnotationLog(JoinPoint joinPoint)
|
||||
{
|
||||
Signature signature = joinPoint.getSignature();
|
||||
MethodSignature methodSignature = (MethodSignature) signature;
|
||||
Method method = methodSignature.getMethod();
|
||||
/**
|
||||
* 数据范围过滤
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @param user 用户
|
||||
* @param userAlias 别名
|
||||
*/
|
||||
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, boolean isUser) {
|
||||
StringBuilder sqlString = new StringBuilder();
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
return method.getAnnotation(DataScope.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// 将 "." 提取出,不写别名为单表查询,写别名为多表查询
|
||||
deptAlias = StrUtil.isNotBlank(deptAlias) ? deptAlias + "." : "";
|
||||
userAlias = StrUtil.isNotBlank(userAlias) ? userAlias + "." : "";
|
||||
|
||||
for (SysRole role : user.getRoles()) {
|
||||
String dataScope = role.getDataScope();
|
||||
if (DATA_SCOPE_ALL.equals(dataScope)) {
|
||||
sqlString = new StringBuilder();
|
||||
break;
|
||||
} else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
|
||||
sqlString.append(StrUtil.format(
|
||||
" OR {}dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ",
|
||||
deptAlias, role.getRoleId()));
|
||||
} else if (DATA_SCOPE_DEPT.equals(dataScope)) {
|
||||
sqlString.append(StrUtil.format(" OR {}dept_id = {} ",
|
||||
deptAlias, user.getDeptId()));
|
||||
} else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
|
||||
sqlString.append(StrUtil.format(
|
||||
" OR {}dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
|
||||
deptAlias, user.getDeptId(), user.getDeptId()));
|
||||
} else if (DATA_SCOPE_SELF.equals(dataScope)) {
|
||||
if (isUser) {
|
||||
sqlString.append(StrUtil.format(" OR {}user_id = {} ",
|
||||
userAlias, user.getUserId()));
|
||||
} else {
|
||||
// 数据权限为仅本人且没有userAlias别名不查询任何数据
|
||||
sqlString.append(" OR 1=0 ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(sqlString.toString())) {
|
||||
putDataScope(joinPoint, sqlString.substring(4));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在注解,如果存在就获取
|
||||
*/
|
||||
private DataScope getAnnotationLog(JoinPoint joinPoint) {
|
||||
Signature signature = joinPoint.getSignature();
|
||||
MethodSignature methodSignature = (MethodSignature) signature;
|
||||
Method method = methodSignature.getMethod();
|
||||
|
||||
if (method != null) {
|
||||
return method.getAnnotation(DataScope.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接权限sql前先清空params.dataScope参数防止注入
|
||||
*/
|
||||
private void clearDataScope(final JoinPoint joinPoint)
|
||||
{
|
||||
private void clearDataScope(final JoinPoint joinPoint) {
|
||||
Object params = joinPoint.getArgs()[0];
|
||||
if (Validator.isNotNull(params))
|
||||
{
|
||||
if (Validator.isNotNull(params)) {
|
||||
putDataScope(joinPoint, "");
|
||||
}
|
||||
}
|
||||
|
||||
private static void putDataScope(JoinPoint joinPoint, String sql) {
|
||||
Object params = joinPoint.getArgs()[0];
|
||||
if (Validator.isNotNull(params))
|
||||
{
|
||||
if(params instanceof BaseEntity) {
|
||||
if (Validator.isNotNull(params)) {
|
||||
if (params instanceof BaseEntity) {
|
||||
BaseEntity baseEntity = (BaseEntity) params;
|
||||
baseEntity.getParams().put(DATA_SCOPE, "");
|
||||
baseEntity.getParams().put(DATA_SCOPE, sql);
|
||||
} else {
|
||||
try {
|
||||
Method getParams = params.getClass().getDeclaredMethod("getParams", null);
|
||||
|
@ -0,0 +1,41 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import com.ruoyi.common.exception.CustomException;
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@EnableAsync
|
||||
@Configuration
|
||||
public class AsyncConfig extends AsyncConfigurerSupport {
|
||||
|
||||
/**
|
||||
* 异步执行需要使用权限框架自带的包装线程池 保证权限信息的传递
|
||||
*/
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
return new DelegatingSecurityContextExecutorService(
|
||||
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步执行异常处理
|
||||
*/
|
||||
@Override
|
||||
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||
return (throwable, method, objects) -> {
|
||||
throwable.printStackTrace();
|
||||
throw new CustomException(
|
||||
"Exception message - " + throwable.getMessage()
|
||||
+ ", Method name - " + method.getName()
|
||||
+ ", Parameter value - " + Arrays.toString(objects));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +1,69 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
|
||||
import com.ruoyi.framework.config.properties.RedissonProperties;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.codec.JsonJacksonCodec;
|
||||
import org.redisson.config.Config;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* redis配置
|
||||
*
|
||||
* @author ruoyi
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class RedisConfig extends CachingConfigurerSupport
|
||||
{
|
||||
@Bean
|
||||
@SuppressWarnings(value = { "unchecked", "rawtypes" })
|
||||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
|
||||
{
|
||||
RedisTemplate<Object, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
public class RedisConfig extends CachingConfigurerSupport {
|
||||
|
||||
GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer();
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
private static final String REDIS_PROTOCOL_PREFIX = "redis://";
|
||||
private static final String REDISS_PROTOCOL_PREFIX = "rediss://";
|
||||
|
||||
// 使用StringRedisSerializer来序列化和反序列化redis的key值
|
||||
template.setKeySerializer(keySerializer);
|
||||
template.setValueSerializer(serializer);
|
||||
@Autowired
|
||||
private RedisProperties redisProperties;
|
||||
|
||||
// Hash的key也采用StringRedisSerializer的序列化方式
|
||||
template.setHashKeySerializer(keySerializer);
|
||||
template.setHashValueSerializer(serializer);
|
||||
@Autowired
|
||||
private RedissonProperties redissonProperties;
|
||||
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
@ConditionalOnMissingBean(RedissonClient.class)
|
||||
public RedissonClient redisson() throws IOException {
|
||||
String prefix = REDIS_PROTOCOL_PREFIX;
|
||||
if (redisProperties.isSsl()) {
|
||||
prefix = REDISS_PROTOCOL_PREFIX;
|
||||
}
|
||||
Config config = new Config();
|
||||
config.setThreads(redissonProperties.getThreads())
|
||||
.setNettyThreads(redissonProperties.getNettyThreads())
|
||||
.setCodec(JsonJacksonCodec.INSTANCE)
|
||||
.setTransportMode(redissonProperties.getTransportMode());
|
||||
|
||||
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
|
||||
// 使用单机模式
|
||||
config.useSingleServer()
|
||||
.setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
|
||||
.setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
|
||||
.setDatabase(redisProperties.getDatabase())
|
||||
.setPassword(redisProperties.getPassword())
|
||||
.setTimeout(singleServerConfig.getTimeout())
|
||||
.setRetryAttempts(singleServerConfig.getRetryAttempts())
|
||||
.setRetryInterval(singleServerConfig.getRetryInterval())
|
||||
.setSubscriptionsPerConnection(singleServerConfig.getSubscriptionsPerConnection())
|
||||
.setClientName(singleServerConfig.getClientName())
|
||||
.setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
|
||||
.setSubscriptionConnectionMinimumIdleSize(singleServerConfig.getSubscriptionConnectionMinimumIdleSize())
|
||||
.setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
|
||||
.setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
|
||||
.setConnectionPoolSize(singleServerConfig.getConnectionPoolSize())
|
||||
.setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval());
|
||||
return Redisson.create(config);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
package com.ruoyi.framework.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.config.TransportMode;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Redisson 配置属性
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "redisson")
|
||||
public class RedissonProperties {
|
||||
|
||||
/**
|
||||
* 线程池数量,默认值 = 当前处理核数量 * 2
|
||||
*/
|
||||
private int threads;
|
||||
|
||||
/**
|
||||
* Netty线程池数量,默认值 = 当前处理核数量 * 2
|
||||
*/
|
||||
private int nettyThreads;
|
||||
|
||||
/**
|
||||
* 传输模式
|
||||
*/
|
||||
private TransportMode transportMode;
|
||||
|
||||
/**
|
||||
* 单机服务配置
|
||||
*/
|
||||
private SingleServerConfig singleServerConfig;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class SingleServerConfig {
|
||||
|
||||
/**
|
||||
* 客户端名称
|
||||
*/
|
||||
private String clientName;
|
||||
|
||||
/**
|
||||
* 最小空闲连接数
|
||||
*/
|
||||
private int connectionMinimumIdleSize;
|
||||
|
||||
/**
|
||||
* 连接池大小
|
||||
*/
|
||||
private int connectionPoolSize;
|
||||
|
||||
/**
|
||||
* 连接空闲超时,单位:毫秒
|
||||
*/
|
||||
private int idleConnectionTimeout;
|
||||
|
||||
/**
|
||||
* 命令等待超时,单位:毫秒
|
||||
*/
|
||||
private int timeout;
|
||||
|
||||
/**
|
||||
* 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。
|
||||
*/
|
||||
private int retryAttempts;
|
||||
|
||||
/**
|
||||
* 命令重试发送时间间隔,单位:毫秒
|
||||
*/
|
||||
private int retryInterval;
|
||||
|
||||
/**
|
||||
* 发布和订阅连接的最小空闲连接数
|
||||
*/
|
||||
private int subscriptionConnectionMinimumIdleSize;
|
||||
|
||||
/**
|
||||
* 发布和订阅连接池大小
|
||||
*/
|
||||
private int subscriptionConnectionPoolSize;
|
||||
|
||||
/**
|
||||
* 单个连接最大订阅数量
|
||||
*/
|
||||
private int subscriptionsPerConnection;
|
||||
|
||||
/**
|
||||
* DNS监测时间间隔,单位:毫秒
|
||||
*/
|
||||
private int dnsMonitoringInterval;
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user