若依 3.0
This commit is contained in:
@ -0,0 +1,122 @@
|
||||
package com.ruoyi.framework.aspectj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.annotation.DataScope;
|
||||
import com.ruoyi.common.base.BaseEntity;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.util.ShiroUtils;
|
||||
import com.ruoyi.system.domain.SysRole;
|
||||
import com.ruoyi.system.domain.SysUser;
|
||||
|
||||
/**
|
||||
* 数据过滤处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class DataScopeAspect
|
||||
{
|
||||
/**
|
||||
* 全部数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_ALL = "1";
|
||||
|
||||
/**
|
||||
* 自定数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_CUSTOM = "2";
|
||||
|
||||
/**
|
||||
* 数据权限过滤关键字
|
||||
*/
|
||||
public static final String DATA_SCOPE = "dataScope";
|
||||
|
||||
// 配置织入点
|
||||
@Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")
|
||||
public void dataScopePointCut()
|
||||
{
|
||||
}
|
||||
|
||||
@Before("dataScopePointCut()")
|
||||
public void doBefore(JoinPoint point) throws Throwable
|
||||
{
|
||||
handleDataScope(point);
|
||||
}
|
||||
|
||||
protected void handleDataScope(final JoinPoint joinPoint)
|
||||
{
|
||||
// 获得注解
|
||||
DataScope controllerDataScope = getAnnotationLog(joinPoint);
|
||||
if (controllerDataScope == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 获取当前的用户
|
||||
SysUser currentUser = ShiroUtils.getUser();
|
||||
if (currentUser != null)
|
||||
{
|
||||
// 如果是超级管理员,则不过滤数据
|
||||
if (!currentUser.isAdmin())
|
||||
{
|
||||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.tableAlias());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据范围过滤
|
||||
*
|
||||
* @param da 部门表别名
|
||||
* @return 标准连接条件对象
|
||||
*/
|
||||
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String alias)
|
||||
{
|
||||
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(StringUtils.format(
|
||||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", alias,
|
||||
role.getRoleId()));
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(sqlString.toString()))
|
||||
{
|
||||
BaseEntity baseEntity = (BaseEntity) joinPoint.getArgs()[0];
|
||||
baseEntity.getParams().put(DATA_SCOPE, " AND (" + 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.ruoyi.framework.aspectj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.annotation.DataSource;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder;
|
||||
|
||||
/**
|
||||
* 多数据源处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Aspect
|
||||
@Order(1)
|
||||
@Component
|
||||
public class DataSourceAspect
|
||||
{
|
||||
protected Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)")
|
||||
public void dsPointCut()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Around("dsPointCut()")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable
|
||||
{
|
||||
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||
|
||||
Method method = signature.getMethod();
|
||||
|
||||
DataSource dataSource = method.getAnnotation(DataSource.class);
|
||||
|
||||
if (StringUtils.isNotNull(dataSource))
|
||||
{
|
||||
DynamicDataSourceContextHolder.setDateSoureType(dataSource.value().name());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return point.proceed();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 销毁数据源 在执行方法之后
|
||||
DynamicDataSourceContextHolder.clearDateSoureType();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
package com.ruoyi.framework.aspectj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.AfterThrowing;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.enums.BusinessStatus;
|
||||
import com.ruoyi.common.json.JSON;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.manager.AsyncManager;
|
||||
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
||||
import com.ruoyi.framework.util.ServletUtils;
|
||||
import com.ruoyi.framework.util.ShiroUtils;
|
||||
import com.ruoyi.system.domain.SysOperLog;
|
||||
import com.ruoyi.system.domain.SysUser;
|
||||
|
||||
/**
|
||||
* 操作日志记录处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class LogAspect
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
|
||||
|
||||
// 配置织入点
|
||||
@Pointcut("@annotation(com.ruoyi.common.annotation.Log)")
|
||||
public void logPointCut()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置通知 用于拦截操作
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
*/
|
||||
@AfterReturning(pointcut = "logPointCut()")
|
||||
public void doBefore(JoinPoint joinPoint)
|
||||
{
|
||||
handleLog(joinPoint, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截异常操作
|
||||
*
|
||||
* @param joinPoint
|
||||
* @param e
|
||||
*/
|
||||
@AfterThrowing(value = "logPointCut()", throwing = "e")
|
||||
public void doAfter(JoinPoint joinPoint, Exception e)
|
||||
{
|
||||
handleLog(joinPoint, e);
|
||||
}
|
||||
|
||||
protected void handleLog(final JoinPoint joinPoint, final Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获得注解
|
||||
Log controllerLog = getAnnotationLog(joinPoint);
|
||||
if (controllerLog == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前的用户
|
||||
SysUser currentUser = ShiroUtils.getUser();
|
||||
|
||||
// *========数据库日志=========*//
|
||||
SysOperLog operLog = new SysOperLog();
|
||||
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
|
||||
// 请求的地址
|
||||
String ip = ShiroUtils.getIp();
|
||||
operLog.setOperIp(ip);
|
||||
|
||||
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
|
||||
if (currentUser != null)
|
||||
{
|
||||
operLog.setOperName(currentUser.getLoginName());
|
||||
if (StringUtils.isNotNull(currentUser.getDept())
|
||||
&& StringUtils.isNotEmpty(currentUser.getDept().getDeptName()))
|
||||
{
|
||||
operLog.setDeptName(currentUser.getDept().getDeptName());
|
||||
}
|
||||
}
|
||||
|
||||
if (e != null)
|
||||
{
|
||||
operLog.setStatus(BusinessStatus.FAIL.ordinal());
|
||||
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
|
||||
}
|
||||
// 设置方法名称
|
||||
String className = joinPoint.getTarget().getClass().getName();
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
operLog.setMethod(className + "." + methodName + "()");
|
||||
// 处理设置注解上的参数
|
||||
getControllerMethodDescription(controllerLog, operLog);
|
||||
// 保存数据库
|
||||
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
// 记录本地异常日志
|
||||
log.error("==前置通知异常==");
|
||||
log.error("异常信息:{}", exp.getMessage());
|
||||
exp.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解中对方法的描述信息 用于Controller层注解
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @return 方法描述
|
||||
* @throws Exception
|
||||
*/
|
||||
public void getControllerMethodDescription(Log log, SysOperLog operLog) throws Exception
|
||||
{
|
||||
// 设置action动作
|
||||
operLog.setBusinessType(log.businessType().ordinal());
|
||||
// 设置标题
|
||||
operLog.setTitle(log.title());
|
||||
// 设置操作人类别
|
||||
operLog.setOperatorType(log.operatorType().ordinal());
|
||||
// 是否需要保存request,参数和值
|
||||
if (log.isSaveRequestData())
|
||||
{
|
||||
// 获取参数的信息,传入到数据库中。
|
||||
setRequestValue(operLog);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求的参数,放到log中
|
||||
*
|
||||
* @param operLog 操作日志
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
private void setRequestValue(SysOperLog operLog) throws Exception
|
||||
{
|
||||
Map<String, String[]> map = ServletUtils.getRequest().getParameterMap();
|
||||
String params = JSON.marshal(map);
|
||||
operLog.setOperParam(StringUtils.substring(params, 0, 255));
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在注解,如果存在就获取
|
||||
*/
|
||||
private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
|
||||
{
|
||||
Signature signature = joinPoint.getSignature();
|
||||
MethodSignature methodSignature = (MethodSignature) signature;
|
||||
Method method = methodSignature.getMethod();
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
return method.getAnnotation(Log.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import java.util.Properties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.google.code.kaptcha.impl.DefaultKaptcha;
|
||||
import com.google.code.kaptcha.util.Config;
|
||||
|
||||
/**
|
||||
* 验证码配置
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
public class CaptchaConfig
|
||||
{
|
||||
@Bean(name = "captchaProducer")
|
||||
public DefaultKaptcha getKaptchaBean()
|
||||
{
|
||||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("kaptcha.border", "yes");
|
||||
properties.setProperty("kaptcha.border.color", "105,179,90");
|
||||
properties.setProperty("kaptcha.textproducer.font.color", "blue");
|
||||
properties.setProperty("kaptcha.image.width", "160");
|
||||
properties.setProperty("kaptcha.image.height", "60");
|
||||
properties.setProperty("kaptcha.textproducer.font.size", "28");
|
||||
properties.setProperty("kaptcha.session.key", "kaptchaCode");
|
||||
properties.setProperty("kaptcha.textproducer.char.spac", "35");
|
||||
properties.setProperty("kaptcha.textproducer.char.length", "5");
|
||||
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
|
||||
properties.setProperty("kaptcha.noise.color", "white");
|
||||
Config config = new Config(properties);
|
||||
defaultKaptcha.setConfig(config);
|
||||
return defaultKaptcha;
|
||||
}
|
||||
|
||||
@Bean(name = "captchaProducerMath")
|
||||
public DefaultKaptcha getKaptchaBeanMath()
|
||||
{
|
||||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("kaptcha.border", "yes");
|
||||
properties.setProperty("kaptcha.border.color", "105,179,90");
|
||||
properties.setProperty("kaptcha.textproducer.font.color", "blue");
|
||||
properties.setProperty("kaptcha.image.width", "160");
|
||||
properties.setProperty("kaptcha.image.height", "60");
|
||||
properties.setProperty("kaptcha.textproducer.font.size", "38");
|
||||
properties.setProperty("kaptcha.session.key", "kaptchaCodeMath");
|
||||
properties.setProperty("kaptcha.textproducer.impl", "com.ruoyi.framework.config.KaptchaTextCreator");
|
||||
properties.setProperty("kaptcha.textproducer.char.spac", "5");
|
||||
properties.setProperty("kaptcha.textproducer.char.length", "6");
|
||||
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
|
||||
properties.setProperty("kaptcha.noise.color", "white");
|
||||
properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
|
||||
properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
|
||||
Config config = new Config(properties);
|
||||
defaultKaptcha.setConfig(config);
|
||||
return defaultKaptcha;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.sql.DataSource;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
|
||||
import com.ruoyi.common.enums.DataSourceType;
|
||||
import com.ruoyi.framework.datasource.DynamicDataSource;
|
||||
|
||||
/**
|
||||
* druid 配置多数据源
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
public class DruidConfig
|
||||
{
|
||||
@Bean
|
||||
@ConfigurationProperties("spring.datasource.druid.master")
|
||||
public DataSource masterDataSource()
|
||||
{
|
||||
return DruidDataSourceBuilder.create().build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("spring.datasource.druid.slave")
|
||||
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
|
||||
public DataSource slaveDataSource()
|
||||
{
|
||||
return DruidDataSourceBuilder.create().build();
|
||||
}
|
||||
|
||||
@Bean(name = "dynamicDataSource")
|
||||
@Primary
|
||||
public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource)
|
||||
{
|
||||
Map<Object, Object> targetDataSources = new HashMap<>();
|
||||
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
|
||||
targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource);
|
||||
return new DynamicDataSource(masterDataSource, targetDataSources);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.DispatcherType;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.xss.XssFilter;
|
||||
|
||||
/**
|
||||
* Filter配置
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
public class FilterConfig
|
||||
{
|
||||
@Value("${xss.enabled}")
|
||||
private String enabled;
|
||||
|
||||
@Value("${xss.excludes}")
|
||||
private String excludes;
|
||||
|
||||
@Value("${xss.urlPatterns}")
|
||||
private String urlPatterns;
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Bean
|
||||
public FilterRegistrationBean xssFilterRegistration()
|
||||
{
|
||||
FilterRegistrationBean registration = new FilterRegistrationBean();
|
||||
registration.setDispatcherTypes(DispatcherType.REQUEST);
|
||||
registration.setFilter(new XssFilter());
|
||||
registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
|
||||
registration.setName("xssFilter");
|
||||
registration.setOrder(Integer.MAX_VALUE);
|
||||
Map<String, String> initParameters = new HashMap<String, String>();
|
||||
initParameters.put("excludes", excludes);
|
||||
initParameters.put("enabled", enabled);
|
||||
registration.setInitParameters(initParameters);
|
||||
return registration;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 读取代码生成相关配置
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "gen")
|
||||
public class GenConfig
|
||||
{
|
||||
/** 作者 */
|
||||
public static String author;
|
||||
/** 生成包路径 */
|
||||
public static String packageName;
|
||||
/** 自动去除表前缀,默认是true */
|
||||
public static String autoRemovePre;
|
||||
/** 表前缀(类名不会包含表前缀) */
|
||||
public static String tablePrefix;
|
||||
|
||||
public static String getAuthor()
|
||||
{
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author)
|
||||
{
|
||||
GenConfig.author = author;
|
||||
}
|
||||
|
||||
public static String getPackageName()
|
||||
{
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public void setPackageName(String packageName)
|
||||
{
|
||||
GenConfig.packageName = packageName;
|
||||
}
|
||||
|
||||
public static String getAutoRemovePre()
|
||||
{
|
||||
return autoRemovePre;
|
||||
}
|
||||
|
||||
public void setAutoRemovePre(String autoRemovePre)
|
||||
{
|
||||
GenConfig.autoRemovePre = autoRemovePre;
|
||||
}
|
||||
|
||||
public static String getTablePrefix()
|
||||
{
|
||||
return tablePrefix;
|
||||
}
|
||||
|
||||
public void setTablePrefix(String tablePrefix)
|
||||
{
|
||||
GenConfig.tablePrefix = tablePrefix;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import java.util.Locale;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||
|
||||
/**
|
||||
* 资源文件配置加载
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
public class I18nConfig implements WebMvcConfigurer
|
||||
{
|
||||
@Bean
|
||||
public LocaleResolver localeResolver()
|
||||
{
|
||||
SessionLocaleResolver slr = new SessionLocaleResolver();
|
||||
// 默认语言
|
||||
slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
|
||||
return slr;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocaleChangeInterceptor localeChangeInterceptor()
|
||||
{
|
||||
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
|
||||
// 参数名
|
||||
lci.setParamName("lang");
|
||||
return lci;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry)
|
||||
{
|
||||
registry.addInterceptor(localeChangeInterceptor());
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import java.util.Random;
|
||||
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
|
||||
|
||||
/**
|
||||
* 验证码文本生成器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class KaptchaTextCreator extends DefaultTextCreator
|
||||
{
|
||||
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
|
||||
|
||||
@Override
|
||||
public String getText()
|
||||
{
|
||||
Integer result = 0;
|
||||
Random random = new Random();
|
||||
int x = random.nextInt(10);
|
||||
int y = random.nextInt(10);
|
||||
StringBuilder suChinese = new StringBuilder();
|
||||
int randomoperands = (int) Math.round(Math.random() * 2);
|
||||
if (randomoperands == 0)
|
||||
{
|
||||
result = x * y;
|
||||
suChinese.append(CNUMBERS[x]);
|
||||
suChinese.append("*");
|
||||
suChinese.append(CNUMBERS[y]);
|
||||
}
|
||||
else if (randomoperands == 1)
|
||||
{
|
||||
if (!(x == 0) && y % x == 0)
|
||||
{
|
||||
result = y / x;
|
||||
suChinese.append(CNUMBERS[y]);
|
||||
suChinese.append("/");
|
||||
suChinese.append(CNUMBERS[x]);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = x + y;
|
||||
suChinese.append(CNUMBERS[x]);
|
||||
suChinese.append("+");
|
||||
suChinese.append(CNUMBERS[y]);
|
||||
}
|
||||
}
|
||||
else if (randomoperands == 2)
|
||||
{
|
||||
if (x >= y)
|
||||
{
|
||||
result = x - y;
|
||||
suChinese.append(CNUMBERS[x]);
|
||||
suChinese.append("-");
|
||||
suChinese.append(CNUMBERS[y]);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = y - x;
|
||||
suChinese.append(CNUMBERS[y]);
|
||||
suChinese.append("-");
|
||||
suChinese.append(CNUMBERS[x]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = x + y;
|
||||
suChinese.append(CNUMBERS[x]);
|
||||
suChinese.append("+");
|
||||
suChinese.append(CNUMBERS[y]);
|
||||
}
|
||||
suChinese.append("=?@" + result);
|
||||
return suChinese.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import com.ruoyi.common.config.Global;
|
||||
|
||||
/**
|
||||
* 通用配置
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
public class ResourcesConfig implements WebMvcConfigurer
|
||||
{
|
||||
/**
|
||||
* 首页地址
|
||||
*/
|
||||
@Value("${shiro.user.indexUrl}")
|
||||
private String indexUrl;
|
||||
|
||||
/**
|
||||
* 默认首页的设置,当输入域名是可以自动跳转到默认指定的网页
|
||||
*/
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry)
|
||||
{
|
||||
registry.addViewController("/").setViewName("forward:" + indexUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry)
|
||||
{
|
||||
/** 文件上传路径 */
|
||||
registry.addResourceHandler("/profile/**").addResourceLocations("file:" + Global.getProfile());
|
||||
|
||||
/** swagger配置 */
|
||||
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
|
||||
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
|
||||
}
|
||||
}
|
@ -0,0 +1,349 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.Filter;
|
||||
import org.apache.shiro.cache.ehcache.EhCacheManager;
|
||||
import org.apache.shiro.codec.Base64;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
||||
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||
import org.apache.shiro.web.mgt.CookieRememberMeManager;
|
||||
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||
import org.apache.shiro.web.servlet.SimpleCookie;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.shiro.realm.UserRealm;
|
||||
import com.ruoyi.framework.shiro.session.OnlineSessionDAO;
|
||||
import com.ruoyi.framework.shiro.session.OnlineSessionFactory;
|
||||
import com.ruoyi.framework.shiro.web.filter.LogoutFilter;
|
||||
import com.ruoyi.framework.shiro.web.filter.captcha.CaptchaValidateFilter;
|
||||
import com.ruoyi.framework.shiro.web.filter.online.OnlineSessionFilter;
|
||||
import com.ruoyi.framework.shiro.web.filter.sync.SyncOnlineSessionFilter;
|
||||
import com.ruoyi.framework.shiro.web.session.OnlineWebSessionManager;
|
||||
import com.ruoyi.framework.shiro.web.session.SpringSessionValidationScheduler;
|
||||
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
|
||||
|
||||
/**
|
||||
* 权限配置加载
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
public class ShiroConfig
|
||||
{
|
||||
public static final String PREMISSION_STRING = "perms[\"{0}\"]";
|
||||
|
||||
// Session超时时间,单位为毫秒(默认30分钟)
|
||||
@Value("${shiro.session.expireTime}")
|
||||
private int expireTime;
|
||||
|
||||
// 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟
|
||||
@Value("${shiro.session.validationInterval}")
|
||||
private int validationInterval;
|
||||
|
||||
// 验证码开关
|
||||
@Value("${shiro.user.captchaEnabled}")
|
||||
private boolean captchaEnabled;
|
||||
|
||||
// 验证码类型
|
||||
@Value("${shiro.user.captchaType}")
|
||||
private String captchaType;
|
||||
|
||||
// 设置Cookie的域名
|
||||
@Value("${shiro.cookie.domain}")
|
||||
private String domain;
|
||||
|
||||
// 设置cookie的有效访问路径
|
||||
@Value("${shiro.cookie.path}")
|
||||
private String path;
|
||||
|
||||
// 设置HttpOnly属性
|
||||
@Value("${shiro.cookie.httpOnly}")
|
||||
private boolean httpOnly;
|
||||
|
||||
// 设置Cookie的过期时间,秒为单位
|
||||
@Value("${shiro.cookie.maxAge}")
|
||||
private int maxAge;
|
||||
|
||||
// 登录地址
|
||||
@Value("${shiro.user.loginUrl}")
|
||||
private String loginUrl;
|
||||
|
||||
// 权限认证失败地址
|
||||
@Value("${shiro.user.unauthorizedUrl}")
|
||||
private String unauthorizedUrl;
|
||||
|
||||
/**
|
||||
* 缓存管理器 使用Ehcache实现
|
||||
*/
|
||||
@Bean
|
||||
public EhCacheManager getEhCacheManager()
|
||||
{
|
||||
net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi");
|
||||
EhCacheManager em = new EhCacheManager();
|
||||
if (StringUtils.isNull(cacheManager))
|
||||
{
|
||||
em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");
|
||||
return em;
|
||||
}
|
||||
else
|
||||
{
|
||||
em.setCacheManager(cacheManager);
|
||||
return em;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义Realm
|
||||
*/
|
||||
@Bean
|
||||
public UserRealm userRealm(EhCacheManager cacheManager)
|
||||
{
|
||||
UserRealm userRealm = new UserRealm();
|
||||
userRealm.setCacheManager(cacheManager);
|
||||
return userRealm;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义sessionDAO会话
|
||||
*/
|
||||
@Bean
|
||||
public OnlineSessionDAO sessionDAO()
|
||||
{
|
||||
OnlineSessionDAO sessionDAO = new OnlineSessionDAO();
|
||||
return sessionDAO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义sessionFactory会话
|
||||
*/
|
||||
@Bean
|
||||
public OnlineSessionFactory sessionFactory()
|
||||
{
|
||||
OnlineSessionFactory sessionFactory = new OnlineSessionFactory();
|
||||
return sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义sessionFactory调度器
|
||||
*/
|
||||
@Bean
|
||||
public SpringSessionValidationScheduler sessionValidationScheduler()
|
||||
{
|
||||
SpringSessionValidationScheduler sessionValidationScheduler = new SpringSessionValidationScheduler();
|
||||
// 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟
|
||||
sessionValidationScheduler.setSessionValidationInterval(validationInterval * 60 * 1000);
|
||||
// 设置会话验证调度器进行会话验证时的会话管理器
|
||||
sessionValidationScheduler.setSessionManager(sessionValidationManager());
|
||||
return sessionValidationScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话管理器
|
||||
*/
|
||||
@Bean
|
||||
public OnlineWebSessionManager sessionValidationManager()
|
||||
{
|
||||
OnlineWebSessionManager manager = new OnlineWebSessionManager();
|
||||
// 加入缓存管理器
|
||||
manager.setCacheManager(getEhCacheManager());
|
||||
// 删除过期的session
|
||||
manager.setDeleteInvalidSessions(true);
|
||||
// 设置全局session超时时间
|
||||
manager.setGlobalSessionTimeout(expireTime * 60 * 1000);
|
||||
// 去掉 JSESSIONID
|
||||
manager.setSessionIdUrlRewritingEnabled(false);
|
||||
// 是否定时检查session
|
||||
manager.setSessionValidationSchedulerEnabled(true);
|
||||
// 自定义SessionDao
|
||||
manager.setSessionDAO(sessionDAO());
|
||||
// 自定义sessionFactory
|
||||
manager.setSessionFactory(sessionFactory());
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话管理器
|
||||
*/
|
||||
@Bean
|
||||
public OnlineWebSessionManager sessionManager()
|
||||
{
|
||||
OnlineWebSessionManager manager = new OnlineWebSessionManager();
|
||||
// 加入缓存管理器
|
||||
manager.setCacheManager(getEhCacheManager());
|
||||
// 删除过期的session
|
||||
manager.setDeleteInvalidSessions(true);
|
||||
// 设置全局session超时时间
|
||||
manager.setGlobalSessionTimeout(expireTime * 60 * 1000);
|
||||
// 去掉 JSESSIONID
|
||||
manager.setSessionIdUrlRewritingEnabled(false);
|
||||
// 定义要使用的无效的Session定时调度器
|
||||
manager.setSessionValidationScheduler(sessionValidationScheduler());
|
||||
// 是否定时检查session
|
||||
manager.setSessionValidationSchedulerEnabled(true);
|
||||
// 自定义SessionDao
|
||||
manager.setSessionDAO(sessionDAO());
|
||||
// 自定义sessionFactory
|
||||
manager.setSessionFactory(sessionFactory());
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全管理器
|
||||
*/
|
||||
@Bean
|
||||
public SecurityManager securityManager(UserRealm userRealm)
|
||||
{
|
||||
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
|
||||
// 设置realm.
|
||||
securityManager.setRealm(userRealm);
|
||||
// 记住我
|
||||
securityManager.setRememberMeManager(rememberMeManager());
|
||||
// 注入缓存管理器;
|
||||
securityManager.setCacheManager(getEhCacheManager());
|
||||
// session管理器
|
||||
securityManager.setSessionManager(sessionManager());
|
||||
return securityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出过滤器
|
||||
*/
|
||||
public LogoutFilter logoutFilter()
|
||||
{
|
||||
LogoutFilter logoutFilter = new LogoutFilter();
|
||||
logoutFilter.setLoginUrl(loginUrl);
|
||||
return logoutFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shiro过滤器配置
|
||||
*/
|
||||
@Bean
|
||||
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
|
||||
{
|
||||
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
|
||||
// Shiro的核心安全接口,这个属性是必须的
|
||||
shiroFilterFactoryBean.setSecurityManager(securityManager);
|
||||
// 身份认证失败,则跳转到登录页面的配置
|
||||
shiroFilterFactoryBean.setLoginUrl(loginUrl);
|
||||
// 权限认证失败,则跳转到指定页面
|
||||
shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
|
||||
// Shiro连接约束配置,即过滤链的定义
|
||||
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
|
||||
// 对静态资源设置匿名访问
|
||||
filterChainDefinitionMap.put("/favicon.ico**", "anon");
|
||||
filterChainDefinitionMap.put("/ruoyi.png**", "anon");
|
||||
filterChainDefinitionMap.put("/css/**", "anon");
|
||||
filterChainDefinitionMap.put("/docs/**", "anon");
|
||||
filterChainDefinitionMap.put("/fonts/**", "anon");
|
||||
filterChainDefinitionMap.put("/img/**", "anon");
|
||||
filterChainDefinitionMap.put("/ajax/**", "anon");
|
||||
filterChainDefinitionMap.put("/js/**", "anon");
|
||||
filterChainDefinitionMap.put("/ruoyi/**", "anon");
|
||||
filterChainDefinitionMap.put("/druid/**", "anon");
|
||||
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
|
||||
// 退出 logout地址,shiro去清除session
|
||||
filterChainDefinitionMap.put("/logout", "logout");
|
||||
// 不需要拦截的访问
|
||||
filterChainDefinitionMap.put("/login", "anon,captchaValidate");
|
||||
// 系统权限列表
|
||||
// filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll());
|
||||
|
||||
Map<String, Filter> filters = new LinkedHashMap<>();
|
||||
filters.put("onlineSession", onlineSessionFilter());
|
||||
filters.put("syncOnlineSession", syncOnlineSessionFilter());
|
||||
filters.put("captchaValidate", captchaValidateFilter());
|
||||
// 注销成功,则跳转到指定页面
|
||||
filters.put("logout", logoutFilter());
|
||||
shiroFilterFactoryBean.setFilters(filters);
|
||||
|
||||
// 所有请求需要认证
|
||||
filterChainDefinitionMap.put("/**", "user,onlineSession,syncOnlineSession");
|
||||
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
|
||||
|
||||
return shiroFilterFactoryBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义在线用户处理过滤器
|
||||
*/
|
||||
@Bean
|
||||
public OnlineSessionFilter onlineSessionFilter()
|
||||
{
|
||||
OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter();
|
||||
onlineSessionFilter.setLoginUrl(loginUrl);
|
||||
return onlineSessionFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义在线用户同步过滤器
|
||||
*/
|
||||
@Bean
|
||||
public SyncOnlineSessionFilter syncOnlineSessionFilter()
|
||||
{
|
||||
SyncOnlineSessionFilter syncOnlineSessionFilter = new SyncOnlineSessionFilter();
|
||||
return syncOnlineSessionFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义验证码过滤器
|
||||
*/
|
||||
@Bean
|
||||
public CaptchaValidateFilter captchaValidateFilter()
|
||||
{
|
||||
CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();
|
||||
captchaValidateFilter.setCaptchaEnabled(captchaEnabled);
|
||||
captchaValidateFilter.setCaptchaType(captchaType);
|
||||
return captchaValidateFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* cookie 属性设置
|
||||
*/
|
||||
public SimpleCookie rememberMeCookie()
|
||||
{
|
||||
SimpleCookie cookie = new SimpleCookie("rememberMe");
|
||||
cookie.setDomain(domain);
|
||||
cookie.setPath(path);
|
||||
cookie.setHttpOnly(httpOnly);
|
||||
cookie.setMaxAge(maxAge * 24 * 60 * 60);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记住我
|
||||
*/
|
||||
public CookieRememberMeManager rememberMeManager()
|
||||
{
|
||||
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
|
||||
cookieRememberMeManager.setCookie(rememberMeCookie());
|
||||
cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ=="));
|
||||
return cookieRememberMeManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* thymeleaf模板引擎和shiro框架的整合
|
||||
*/
|
||||
@Bean
|
||||
public ShiroDialect shiroDialect()
|
||||
{
|
||||
return new ShiroDialect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启Shiro注解通知器
|
||||
*/
|
||||
@Bean
|
||||
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
|
||||
@Qualifier("securityManager") SecurityManager securityManager)
|
||||
{
|
||||
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
|
||||
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
|
||||
return authorizationAttributeSourceAdvisor;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.ruoyi.framework.datasource;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.sql.DataSource;
|
||||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
|
||||
|
||||
/**
|
||||
* 动态数据源
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class DynamicDataSource extends AbstractRoutingDataSource
|
||||
{
|
||||
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
|
||||
{
|
||||
super.setDefaultTargetDataSource(defaultTargetDataSource);
|
||||
super.setTargetDataSources(targetDataSources);
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object determineCurrentLookupKey()
|
||||
{
|
||||
return DynamicDataSourceContextHolder.getDateSoureType();
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.ruoyi.framework.datasource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 数据源切换处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class DynamicDataSourceContextHolder
|
||||
{
|
||||
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
|
||||
|
||||
/**
|
||||
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
|
||||
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
|
||||
*/
|
||||
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 设置数据源的变量
|
||||
*/
|
||||
public static void setDateSoureType(String dsType)
|
||||
{
|
||||
log.info("切换到{}数据源", dsType);
|
||||
CONTEXT_HOLDER.set(dsType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得数据源的变量
|
||||
*/
|
||||
public static String getDateSoureType()
|
||||
{
|
||||
return CONTEXT_HOLDER.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空数据源变量
|
||||
*/
|
||||
public static void clearDateSoureType()
|
||||
{
|
||||
CONTEXT_HOLDER.remove();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.ruoyi.framework.manager;
|
||||
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 异步任务管理器
|
||||
*
|
||||
* @author liuhulu
|
||||
*/
|
||||
public class AsyncManager
|
||||
{
|
||||
/**
|
||||
* 操作延迟10毫秒
|
||||
*/
|
||||
private final int OPERATE_DELAY_TIME = 10;
|
||||
|
||||
/**
|
||||
* 异步操作任务调度线程池
|
||||
*/
|
||||
private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
|
||||
|
||||
/**
|
||||
* 单例模式
|
||||
*/
|
||||
private static AsyncManager me = new AsyncManager();
|
||||
|
||||
public static AsyncManager me()
|
||||
{
|
||||
return me;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*
|
||||
* @param 任务task
|
||||
*/
|
||||
public void execute(TimerTask task)
|
||||
{
|
||||
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
package com.ruoyi.framework.manager.factory;
|
||||
|
||||
import java.util.TimerTask;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.utils.AddressUtils;
|
||||
import com.ruoyi.framework.shiro.session.OnlineSession;
|
||||
import com.ruoyi.framework.util.LogUtils;
|
||||
import com.ruoyi.framework.util.ServletUtils;
|
||||
import com.ruoyi.framework.util.ShiroUtils;
|
||||
import com.ruoyi.framework.util.SpringUtils;
|
||||
import com.ruoyi.system.domain.SysLogininfor;
|
||||
import com.ruoyi.system.domain.SysOperLog;
|
||||
import com.ruoyi.system.domain.SysUserOnline;
|
||||
import com.ruoyi.system.service.ISysOperLogService;
|
||||
import com.ruoyi.system.service.impl.SysLogininforServiceImpl;
|
||||
import com.ruoyi.system.service.impl.SysUserOnlineServiceImpl;
|
||||
import eu.bitwalker.useragentutils.UserAgent;
|
||||
|
||||
/**
|
||||
* 异步工厂(产生任务用)
|
||||
*
|
||||
* @author liuhulu
|
||||
*
|
||||
*/
|
||||
public class AsyncFactory
|
||||
{
|
||||
private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");
|
||||
|
||||
/**
|
||||
* 同步session到数据库
|
||||
*
|
||||
* @param session 在线用户会话
|
||||
* @return 任务task
|
||||
*/
|
||||
public static TimerTask syncSessionToDb(final OnlineSession session)
|
||||
{
|
||||
return new TimerTask()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
SysUserOnline online = new SysUserOnline();
|
||||
online.setSessionId(String.valueOf(session.getId()));
|
||||
online.setDeptName(session.getDeptName());
|
||||
online.setLoginName(session.getLoginName());
|
||||
online.setStartTimestamp(session.getStartTimestamp());
|
||||
online.setLastAccessTime(session.getLastAccessTime());
|
||||
online.setExpireTime(session.getTimeout());
|
||||
online.setIpaddr(session.getHost());
|
||||
online.setLoginLocation(AddressUtils.getRealAddressByIP(session.getHost()));
|
||||
online.setBrowser(session.getBrowser());
|
||||
online.setOs(session.getOs());
|
||||
online.setStatus(session.getStatus());
|
||||
SpringUtils.getBean(SysUserOnlineServiceImpl.class).saveOnline(online);
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作日志记录
|
||||
*
|
||||
* @param operLog 操作日志信息
|
||||
* @return 任务task
|
||||
*/
|
||||
public static TimerTask recordOper(final SysOperLog operLog)
|
||||
{
|
||||
return new TimerTask()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
// 远程查询操作地点
|
||||
operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
|
||||
SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登陆信息
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param status 状态
|
||||
* @param message 消息
|
||||
* @param args 列表
|
||||
* @return 任务task
|
||||
*/
|
||||
public static TimerTask recordLogininfor(final String username, final String status, final String message, final Object... args)
|
||||
{
|
||||
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
final String ip = ShiroUtils.getIp();
|
||||
return new TimerTask()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append(LogUtils.getBlock(ip));
|
||||
s.append(AddressUtils.getRealAddressByIP(ip));
|
||||
s.append(LogUtils.getBlock(username));
|
||||
s.append(LogUtils.getBlock(status));
|
||||
s.append(LogUtils.getBlock(message));
|
||||
// 打印信息到日志
|
||||
sys_user_logger.info(s.toString(), args);
|
||||
// 获取客户端操作系统
|
||||
String os = userAgent.getOperatingSystem().getName();
|
||||
// 获取客户端浏览器
|
||||
String browser = userAgent.getBrowser().getName();
|
||||
// 封装对象
|
||||
SysLogininfor logininfor = new SysLogininfor();
|
||||
logininfor.setLoginName(username);
|
||||
logininfor.setIpaddr(ip);
|
||||
logininfor.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||
logininfor.setBrowser(browser);
|
||||
logininfor.setOs(os);
|
||||
logininfor.setMsg(message);
|
||||
// 日志状态
|
||||
if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status))
|
||||
{
|
||||
logininfor.setStatus(Constants.SUCCESS);
|
||||
}
|
||||
else if (Constants.LOGIN_FAIL.equals(status))
|
||||
{
|
||||
logininfor.setStatus(Constants.FAIL);
|
||||
}
|
||||
// 插入数据
|
||||
SpringUtils.getBean(SysLogininforServiceImpl.class).insertLogininfor(logininfor);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
package com.ruoyi.framework.shiro.realm;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.ExcessiveAttemptsException;
|
||||
import org.apache.shiro.authc.IncorrectCredentialsException;
|
||||
import org.apache.shiro.authc.LockedAccountException;
|
||||
import org.apache.shiro.authc.SimpleAuthenticationInfo;
|
||||
import org.apache.shiro.authc.UnknownAccountException;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.ruoyi.framework.shiro.service.LoginService;
|
||||
import com.ruoyi.framework.util.ShiroUtils;
|
||||
import com.ruoyi.framework.web.exception.user.CaptchaException;
|
||||
import com.ruoyi.framework.web.exception.user.RoleBlockedException;
|
||||
import com.ruoyi.framework.web.exception.user.UserBlockedException;
|
||||
import com.ruoyi.framework.web.exception.user.UserNotExistsException;
|
||||
import com.ruoyi.framework.web.exception.user.UserPasswordNotMatchException;
|
||||
import com.ruoyi.framework.web.exception.user.UserPasswordRetryLimitExceedException;
|
||||
import com.ruoyi.system.domain.SysUser;
|
||||
import com.ruoyi.system.service.ISysMenuService;
|
||||
import com.ruoyi.system.service.ISysRoleService;
|
||||
|
||||
/**
|
||||
* 自定义Realm 处理登录 权限
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserRealm extends AuthorizingRealm
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(UserRealm.class);
|
||||
|
||||
@Autowired
|
||||
private ISysMenuService menuService;
|
||||
|
||||
@Autowired
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@Autowired
|
||||
private LoginService loginService;
|
||||
|
||||
/**
|
||||
* 授权
|
||||
*/
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0)
|
||||
{
|
||||
SysUser user = ShiroUtils.getUser();
|
||||
// 角色列表
|
||||
Set<String> roles = new HashSet<String>();
|
||||
// 功能列表
|
||||
Set<String> menus = new HashSet<String>();
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
|
||||
// 管理员拥有所有权限
|
||||
if (user.isAdmin())
|
||||
{
|
||||
info.addRole("admin");
|
||||
info.addStringPermission("*:*:*");
|
||||
}
|
||||
else
|
||||
{
|
||||
roles = roleService.selectRoleKeys(user.getUserId());
|
||||
menus = menuService.selectPermsByUserId(user.getUserId());
|
||||
// 角色加入AuthorizationInfo认证对象
|
||||
info.setRoles(roles);
|
||||
// 权限加入AuthorizationInfo认证对象
|
||||
info.setStringPermissions(menus);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录认证
|
||||
*/
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
|
||||
{
|
||||
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
|
||||
String username = upToken.getUsername();
|
||||
String password = "";
|
||||
if (upToken.getPassword() != null)
|
||||
{
|
||||
password = new String(upToken.getPassword());
|
||||
}
|
||||
|
||||
SysUser user = null;
|
||||
try
|
||||
{
|
||||
user = loginService.login(username, password);
|
||||
}
|
||||
catch (CaptchaException e)
|
||||
{
|
||||
throw new AuthenticationException(e.getMessage(), e);
|
||||
}
|
||||
catch (UserNotExistsException e)
|
||||
{
|
||||
throw new UnknownAccountException(e.getMessage(), e);
|
||||
}
|
||||
catch (UserPasswordNotMatchException e)
|
||||
{
|
||||
throw new IncorrectCredentialsException(e.getMessage(), e);
|
||||
}
|
||||
catch (UserPasswordRetryLimitExceedException e)
|
||||
{
|
||||
throw new ExcessiveAttemptsException(e.getMessage(), e);
|
||||
}
|
||||
catch (UserBlockedException e)
|
||||
{
|
||||
throw new LockedAccountException(e.getMessage(), e);
|
||||
}
|
||||
catch (RoleBlockedException e)
|
||||
{
|
||||
throw new LockedAccountException(e.getMessage(), e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
|
||||
throw new AuthenticationException(e.getMessage(), e);
|
||||
}
|
||||
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理缓存权限
|
||||
*/
|
||||
public void clearCachedAuthorizationInfo()
|
||||
{
|
||||
this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
package com.ruoyi.framework.shiro.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.ShiroConstants;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.enums.UserStatus;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.framework.manager.AsyncManager;
|
||||
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
||||
import com.ruoyi.framework.util.MessageUtils;
|
||||
import com.ruoyi.framework.util.ServletUtils;
|
||||
import com.ruoyi.framework.util.ShiroUtils;
|
||||
import com.ruoyi.framework.web.exception.user.CaptchaException;
|
||||
import com.ruoyi.framework.web.exception.user.UserBlockedException;
|
||||
import com.ruoyi.framework.web.exception.user.UserDeleteException;
|
||||
import com.ruoyi.framework.web.exception.user.UserNotExistsException;
|
||||
import com.ruoyi.framework.web.exception.user.UserPasswordNotMatchException;
|
||||
import com.ruoyi.system.domain.SysUser;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
|
||||
/**
|
||||
* 登录校验方法
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class LoginService
|
||||
{
|
||||
@Autowired
|
||||
private PasswordService passwordService;
|
||||
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
public SysUser login(String username, String password)
|
||||
{
|
||||
// 验证码校验
|
||||
if (!StringUtils.isEmpty(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA)))
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
|
||||
throw new CaptchaException();
|
||||
}
|
||||
// 用户名或密码为空 错误
|
||||
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
|
||||
throw new UserNotExistsException();
|
||||
}
|
||||
// 密码如果不在指定范围内 错误
|
||||
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|
||||
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
|
||||
throw new UserPasswordNotMatchException();
|
||||
}
|
||||
|
||||
// 用户名不在指定范围内 错误
|
||||
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|
||||
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
|
||||
throw new UserPasswordNotMatchException();
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
SysUser user = userService.selectUserByLoginName(username);
|
||||
|
||||
if (user == null && maybeMobilePhoneNumber(username))
|
||||
{
|
||||
user = userService.selectUserByPhoneNumber(username);
|
||||
}
|
||||
|
||||
if (user == null && maybeEmail(username))
|
||||
{
|
||||
user = userService.selectUserByEmail(username);
|
||||
}
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));
|
||||
throw new UserNotExistsException();
|
||||
}
|
||||
|
||||
if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete")));
|
||||
throw new UserDeleteException();
|
||||
}
|
||||
|
||||
if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked", user.getRemark())));
|
||||
throw new UserBlockedException(user.getRemark());
|
||||
}
|
||||
|
||||
passwordService.validate(user, password);
|
||||
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
|
||||
recordLoginInfo(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
private boolean maybeEmail(String username)
|
||||
{
|
||||
if (!username.matches(UserConstants.EMAIL_PATTERN))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean maybeMobilePhoneNumber(String username)
|
||||
{
|
||||
if (!username.matches(UserConstants.MOBILE_PHONE_NUMBER_PATTERN))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*/
|
||||
public void recordLoginInfo(SysUser user)
|
||||
{
|
||||
user.setLoginIp(ShiroUtils.getIp());
|
||||
user.setLoginDate(DateUtils.getNowDate());
|
||||
userService.updateUserInfo(user);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package com.ruoyi.framework.shiro.service;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.annotation.PostConstruct;
|
||||
import org.apache.shiro.cache.Cache;
|
||||
import org.apache.shiro.cache.CacheManager;
|
||||
import org.apache.shiro.crypto.hash.Md5Hash;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.framework.manager.AsyncManager;
|
||||
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
||||
import com.ruoyi.framework.util.MessageUtils;
|
||||
import com.ruoyi.framework.web.exception.user.UserPasswordNotMatchException;
|
||||
import com.ruoyi.framework.web.exception.user.UserPasswordRetryLimitExceedException;
|
||||
import com.ruoyi.system.domain.SysUser;
|
||||
|
||||
/**
|
||||
* 登录密码方法
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class PasswordService
|
||||
{
|
||||
@Autowired
|
||||
private CacheManager cacheManager;
|
||||
|
||||
private Cache<String, AtomicInteger> loginRecordCache;
|
||||
|
||||
@Value(value = "${user.password.maxRetryCount}")
|
||||
private String maxRetryCount;
|
||||
|
||||
@PostConstruct
|
||||
public void init()
|
||||
{
|
||||
loginRecordCache = cacheManager.getCache("loginRecordCache");
|
||||
}
|
||||
|
||||
public void validate(SysUser user, String password)
|
||||
{
|
||||
String loginName = user.getLoginName();
|
||||
|
||||
AtomicInteger retryCount = loginRecordCache.get(loginName);
|
||||
|
||||
if (retryCount == null)
|
||||
{
|
||||
retryCount = new AtomicInteger(0);
|
||||
loginRecordCache.put(loginName, retryCount);
|
||||
}
|
||||
if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount).intValue())
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount)));
|
||||
throw new UserPasswordRetryLimitExceedException(Integer.valueOf(maxRetryCount).intValue());
|
||||
}
|
||||
|
||||
if (!matches(user, password))
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", retryCount)));
|
||||
loginRecordCache.put(loginName, retryCount);
|
||||
throw new UserPasswordNotMatchException();
|
||||
}
|
||||
else
|
||||
{
|
||||
clearLoginRecordCache(loginName);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches(SysUser user, String newPassword)
|
||||
{
|
||||
return user.getPassword().equals(encryptPassword(user.getLoginName(), newPassword, user.getSalt()));
|
||||
}
|
||||
|
||||
public void clearLoginRecordCache(String username)
|
||||
{
|
||||
loginRecordCache.remove(username);
|
||||
}
|
||||
|
||||
public String encryptPassword(String username, String password, String salt)
|
||||
{
|
||||
return new Md5Hash(username + password + salt).toHex().toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
System.out.println(new PasswordService().encryptPassword("admin", "admin123", "111111"));
|
||||
System.out.println(new PasswordService().encryptPassword("ry", "admin123", "222222"));
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
package com.ruoyi.framework.shiro.session;
|
||||
|
||||
import org.apache.shiro.session.mgt.SimpleSession;
|
||||
import com.ruoyi.common.enums.OnlineStatus;
|
||||
|
||||
/**
|
||||
* 在线用户会话属性
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class OnlineSession extends SimpleSession
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 用户ID */
|
||||
private Long userId;
|
||||
|
||||
/** 用户名称 */
|
||||
private String loginName;
|
||||
|
||||
/** 部门名称 */
|
||||
private String deptName;
|
||||
|
||||
/** 登录IP地址 */
|
||||
private String host;
|
||||
|
||||
/** 浏览器类型 */
|
||||
private String browser;
|
||||
|
||||
/** 操作系统 */
|
||||
private String os;
|
||||
|
||||
/** 在线状态 */
|
||||
private OnlineStatus status = OnlineStatus.on_line;
|
||||
|
||||
/** 属性是否改变 优化session数据同步 */
|
||||
private transient boolean attributeChanged = false;
|
||||
|
||||
@Override
|
||||
public String getHost()
|
||||
{
|
||||
return host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHost(String host)
|
||||
{
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public String getBrowser()
|
||||
{
|
||||
return browser;
|
||||
}
|
||||
|
||||
public void setBrowser(String browser)
|
||||
{
|
||||
this.browser = browser;
|
||||
}
|
||||
|
||||
public String getOs()
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
public void setOs(String os)
|
||||
{
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public Long getUserId()
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getLoginName()
|
||||
{
|
||||
return loginName;
|
||||
}
|
||||
|
||||
public void setLoginName(String loginName)
|
||||
{
|
||||
this.loginName = loginName;
|
||||
}
|
||||
|
||||
public String getDeptName()
|
||||
{
|
||||
return deptName;
|
||||
}
|
||||
|
||||
public void setDeptName(String deptName)
|
||||
{
|
||||
this.deptName = deptName;
|
||||
}
|
||||
|
||||
public OnlineStatus getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(OnlineStatus status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void markAttributeChanged()
|
||||
{
|
||||
this.attributeChanged = true;
|
||||
}
|
||||
|
||||
public void resetAttributeChanged()
|
||||
{
|
||||
this.attributeChanged = false;
|
||||
}
|
||||
|
||||
public boolean isAttributeChanged()
|
||||
{
|
||||
return attributeChanged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(Object key, Object value)
|
||||
{
|
||||
super.setAttribute(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removeAttribute(Object key)
|
||||
{
|
||||
return super.removeAttribute(key);
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package com.ruoyi.framework.shiro.session;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import org.apache.shiro.session.Session;
|
||||
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import com.ruoyi.common.enums.OnlineStatus;
|
||||
import com.ruoyi.framework.manager.AsyncManager;
|
||||
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
||||
import com.ruoyi.system.domain.SysUserOnline;
|
||||
import com.ruoyi.system.service.impl.SysUserOnlineServiceImpl;
|
||||
|
||||
/**
|
||||
* 针对自定义的ShiroSession的db操作
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class OnlineSessionDAO extends EnterpriseCacheSessionDAO
|
||||
{
|
||||
/**
|
||||
* 同步session到数据库的周期 单位为毫秒(默认1分钟)
|
||||
*/
|
||||
@Value("${shiro.session.dbSyncPeriod}")
|
||||
private int dbSyncPeriod;
|
||||
|
||||
/**
|
||||
* 上次同步数据库的时间戳
|
||||
*/
|
||||
private static final String LAST_SYNC_DB_TIMESTAMP = OnlineSessionDAO.class.getName() + "LAST_SYNC_DB_TIMESTAMP";
|
||||
|
||||
@Autowired
|
||||
private SysUserOnlineServiceImpl onlineService;
|
||||
|
||||
public OnlineSessionDAO()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public OnlineSessionDAO(long expireTime)
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话ID获取会话
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return ShiroSession
|
||||
*/
|
||||
@Override
|
||||
protected Session doReadSession(Serializable sessionId)
|
||||
{
|
||||
SysUserOnline userOnline = onlineService.selectOnlineById(String.valueOf(sessionId));
|
||||
if (userOnline == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return super.doReadSession(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会话;如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
|
||||
*/
|
||||
public void syncToDb(OnlineSession onlineSession)
|
||||
{
|
||||
Date lastSyncTimestamp = (Date) onlineSession.getAttribute(LAST_SYNC_DB_TIMESTAMP);
|
||||
if (lastSyncTimestamp != null)
|
||||
{
|
||||
boolean needSync = true;
|
||||
long deltaTime = onlineSession.getLastAccessTime().getTime() - lastSyncTimestamp.getTime();
|
||||
if (deltaTime < dbSyncPeriod * 60 * 1000)
|
||||
{
|
||||
// 时间差不足 无需同步
|
||||
needSync = false;
|
||||
}
|
||||
boolean isGuest = onlineSession.getUserId() == null || onlineSession.getUserId() == 0L;
|
||||
|
||||
// session 数据变更了 同步
|
||||
if (isGuest == false && onlineSession.isAttributeChanged())
|
||||
{
|
||||
needSync = true;
|
||||
}
|
||||
|
||||
if (needSync == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
onlineSession.setAttribute(LAST_SYNC_DB_TIMESTAMP, onlineSession.getLastAccessTime());
|
||||
// 更新完后 重置标识
|
||||
if (onlineSession.isAttributeChanged())
|
||||
{
|
||||
onlineSession.resetAttributeChanged();
|
||||
}
|
||||
AsyncManager.me().execute(AsyncFactory.syncSessionToDb(onlineSession));
|
||||
}
|
||||
|
||||
/**
|
||||
* 当会话过期/停止(如用户退出时)属性等会调用
|
||||
*/
|
||||
@Override
|
||||
protected void doDelete(Session session)
|
||||
{
|
||||
OnlineSession onlineSession = (OnlineSession) session;
|
||||
if (null == onlineSession)
|
||||
{
|
||||
return;
|
||||
}
|
||||
onlineSession.setStatus(OnlineStatus.off_line);
|
||||
onlineService.deleteOnlineById(String.valueOf(onlineSession.getId()));
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.ruoyi.framework.shiro.session;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.shiro.session.Session;
|
||||
import org.apache.shiro.session.mgt.SessionContext;
|
||||
import org.apache.shiro.session.mgt.SessionFactory;
|
||||
import org.apache.shiro.web.session.mgt.WebSessionContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.utils.IpUtils;
|
||||
import com.ruoyi.framework.util.ServletUtils;
|
||||
import eu.bitwalker.useragentutils.UserAgent;
|
||||
|
||||
/**
|
||||
* 自定义sessionFactory会话
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class OnlineSessionFactory implements SessionFactory
|
||||
{
|
||||
@Override
|
||||
public Session createSession(SessionContext initData)
|
||||
{
|
||||
OnlineSession session = new OnlineSession();
|
||||
if (initData != null && initData instanceof WebSessionContext)
|
||||
{
|
||||
WebSessionContext sessionContext = (WebSessionContext) initData;
|
||||
HttpServletRequest request = (HttpServletRequest) sessionContext.getServletRequest();
|
||||
if (request != null)
|
||||
{
|
||||
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
// 获取客户端操作系统
|
||||
String os = userAgent.getOperatingSystem().getName();
|
||||
// 获取客户端浏览器
|
||||
String browser = userAgent.getBrowser().getName();
|
||||
session.setHost(IpUtils.getIpAddr(request));
|
||||
session.setBrowser(browser);
|
||||
session.setOs(os);
|
||||
}
|
||||
}
|
||||
return session;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.ruoyi.framework.shiro.web.filter;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import org.apache.shiro.session.SessionException;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.manager.AsyncManager;
|
||||
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
||||
import com.ruoyi.framework.util.MessageUtils;
|
||||
import com.ruoyi.framework.util.ShiroUtils;
|
||||
import com.ruoyi.system.domain.SysUser;
|
||||
|
||||
/**
|
||||
* 退出过滤器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(LogoutFilter.class);
|
||||
|
||||
/**
|
||||
* 退出后重定向的地址
|
||||
*/
|
||||
private String loginUrl;
|
||||
|
||||
public String getLoginUrl()
|
||||
{
|
||||
return loginUrl;
|
||||
}
|
||||
|
||||
public void setLoginUrl(String loginUrl)
|
||||
{
|
||||
this.loginUrl = loginUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
Subject subject = getSubject(request, response);
|
||||
String redirectUrl = getRedirectUrl(request, response, subject);
|
||||
try
|
||||
{
|
||||
SysUser user = ShiroUtils.getUser();
|
||||
if (StringUtils.isNotNull(user))
|
||||
{
|
||||
String loginName = user.getLoginName();
|
||||
// 记录用户退出日志
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
|
||||
}
|
||||
// 退出登录
|
||||
subject.logout();
|
||||
}
|
||||
catch (SessionException ise)
|
||||
{
|
||||
log.error("logout fail.", ise);
|
||||
}
|
||||
issueRedirect(request, response, redirectUrl);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("Encountered session exception during logout. This can generally safely be ignored.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出跳转URL
|
||||
*/
|
||||
@Override
|
||||
protected String getRedirectUrl(ServletRequest request, ServletResponse response, Subject subject)
|
||||
{
|
||||
String url = getLoginUrl();
|
||||
if (StringUtils.isNotEmpty(url))
|
||||
{
|
||||
return url;
|
||||
}
|
||||
return super.getRedirectUrl(request, response, subject);
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package com.ruoyi.framework.shiro.web.filter.captcha;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.shiro.web.filter.AccessControlFilter;
|
||||
import com.google.code.kaptcha.Constants;
|
||||
import com.ruoyi.common.constant.ShiroConstants;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.util.ShiroUtils;
|
||||
|
||||
/**
|
||||
* 验证码过滤器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class CaptchaValidateFilter extends AccessControlFilter
|
||||
{
|
||||
/**
|
||||
* 是否开启验证码
|
||||
*/
|
||||
private boolean captchaEnabled = true;
|
||||
|
||||
/**
|
||||
* 验证码类型
|
||||
*/
|
||||
private String captchaType = "math";
|
||||
|
||||
public void setCaptchaEnabled(boolean captchaEnabled)
|
||||
{
|
||||
this.captchaEnabled = captchaEnabled;
|
||||
}
|
||||
|
||||
public void setCaptchaType(String captchaType)
|
||||
{
|
||||
this.captchaType = captchaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception
|
||||
{
|
||||
request.setAttribute(ShiroConstants.CURRENT_ENABLED, captchaEnabled);
|
||||
request.setAttribute(ShiroConstants.CURRENT_TYPE, captchaType);
|
||||
return super.onPreHandle(request, response, mappedValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
|
||||
throws Exception
|
||||
{
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
// 验证码禁用 或不是表单提交 允许访问
|
||||
if (captchaEnabled == false || !"post".equals(httpServletRequest.getMethod().toLowerCase()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return validateResponse(httpServletRequest, httpServletRequest.getParameter(ShiroConstants.CURRENT_VALIDATECODE));
|
||||
}
|
||||
|
||||
public boolean validateResponse(HttpServletRequest request, String validateCode)
|
||||
{
|
||||
Object obj = ShiroUtils.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
|
||||
String code = String.valueOf(obj != null ? obj : "");
|
||||
if (StringUtils.isEmpty(validateCode) || !validateCode.equalsIgnoreCase(code))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception
|
||||
{
|
||||
request.setAttribute(ShiroConstants.CURRENT_CAPTCHA, ShiroConstants.CAPTCHA_ERROR);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package com.ruoyi.framework.shiro.web.filter.online;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import org.apache.shiro.session.Session;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.web.filter.AccessControlFilter;
|
||||
import org.apache.shiro.web.util.WebUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import com.ruoyi.common.constant.ShiroConstants;
|
||||
import com.ruoyi.common.enums.OnlineStatus;
|
||||
import com.ruoyi.framework.shiro.session.OnlineSession;
|
||||
import com.ruoyi.framework.shiro.session.OnlineSessionDAO;
|
||||
import com.ruoyi.framework.util.ShiroUtils;
|
||||
import com.ruoyi.system.domain.SysUser;
|
||||
|
||||
/**
|
||||
* 自定义访问控制
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class OnlineSessionFilter extends AccessControlFilter
|
||||
{
|
||||
/**
|
||||
* 强制退出后重定向的地址
|
||||
*/
|
||||
@Value("${shiro.user.loginUrl}")
|
||||
private String loginUrl;
|
||||
|
||||
@Autowired
|
||||
private OnlineSessionDAO onlineSessionDAO;
|
||||
|
||||
/**
|
||||
* 表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false;
|
||||
*/
|
||||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
|
||||
throws Exception
|
||||
{
|
||||
Subject subject = getSubject(request, response);
|
||||
if (subject == null || subject.getSession() == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
Session session = onlineSessionDAO.readSession(subject.getSession().getId());
|
||||
if (session != null && session instanceof OnlineSession)
|
||||
{
|
||||
OnlineSession onlineSession = (OnlineSession) session;
|
||||
request.setAttribute(ShiroConstants.ONLINE_SESSION, onlineSession);
|
||||
// 把user对象设置进去
|
||||
boolean isGuest = onlineSession.getUserId() == null || onlineSession.getUserId() == 0L;
|
||||
if (isGuest == true)
|
||||
{
|
||||
SysUser user = ShiroUtils.getUser();
|
||||
if (user != null)
|
||||
{
|
||||
onlineSession.setUserId(user.getUserId());
|
||||
onlineSession.setLoginName(user.getLoginName());
|
||||
onlineSession.setDeptName(user.getDept().getDeptName());
|
||||
onlineSession.markAttributeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
if (onlineSession.getStatus() == OnlineStatus.off_line)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理了,将直接返回即可。
|
||||
*/
|
||||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception
|
||||
{
|
||||
Subject subject = getSubject(request, response);
|
||||
if (subject != null)
|
||||
{
|
||||
subject.logout();
|
||||
}
|
||||
saveRequestAndRedirectToLogin(request, response);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 跳转到登录页
|
||||
@Override
|
||||
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException
|
||||
{
|
||||
WebUtils.issueRedirect(request, response, loginUrl);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.ruoyi.framework.shiro.web.filter.sync;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import org.apache.shiro.web.filter.PathMatchingFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.ruoyi.common.constant.ShiroConstants;
|
||||
import com.ruoyi.framework.shiro.session.OnlineSession;
|
||||
import com.ruoyi.framework.shiro.session.OnlineSessionDAO;
|
||||
|
||||
/**
|
||||
* 同步Session数据到Db
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SyncOnlineSessionFilter extends PathMatchingFilter
|
||||
{
|
||||
@Autowired
|
||||
private OnlineSessionDAO onlineSessionDAO;
|
||||
|
||||
/**
|
||||
* 同步会话数据到DB 一次请求最多同步一次 防止过多处理 需要放到Shiro过滤器之前
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception
|
||||
{
|
||||
OnlineSession session = (OnlineSession) request.getAttribute(ShiroConstants.ONLINE_SESSION);
|
||||
// 如果session stop了 也不同步
|
||||
// session停止时间,如果stopTimestamp不为null,则代表已停止
|
||||
if (session != null && session.getUserId() != null && session.getStopTimestamp() == null)
|
||||
{
|
||||
onlineSessionDAO.syncToDb(session);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package com.ruoyi.framework.shiro.web.session;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.shiro.session.ExpiredSessionException;
|
||||
import org.apache.shiro.session.InvalidSessionException;
|
||||
import org.apache.shiro.session.Session;
|
||||
import org.apache.shiro.session.mgt.DefaultSessionKey;
|
||||
import org.apache.shiro.session.mgt.SessionKey;
|
||||
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.ruoyi.common.constant.ShiroConstants;
|
||||
import com.ruoyi.framework.shiro.session.OnlineSession;
|
||||
import com.ruoyi.framework.util.SpringUtils;
|
||||
import com.ruoyi.system.domain.SysUserOnline;
|
||||
import com.ruoyi.system.service.impl.SysUserOnlineServiceImpl;
|
||||
|
||||
/**
|
||||
* 主要是在此如果会话的属性修改了 就标识下其修改了 然后方便 OnlineSessionDao同步
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class OnlineWebSessionManager extends DefaultWebSessionManager
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(OnlineWebSessionManager.class);
|
||||
|
||||
@Override
|
||||
public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException
|
||||
{
|
||||
super.setAttribute(sessionKey, attributeKey, value);
|
||||
if (value != null && needMarkAttributeChanged(attributeKey))
|
||||
{
|
||||
OnlineSession s = (OnlineSession) doGetSession(sessionKey);
|
||||
s.markAttributeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needMarkAttributeChanged(Object attributeKey)
|
||||
{
|
||||
if (attributeKey == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
String attributeKeyStr = attributeKey.toString();
|
||||
// 优化 flash属性没必要持久化
|
||||
if (attributeKeyStr.startsWith("org.springframework"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (attributeKeyStr.startsWith("javax.servlet"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (attributeKeyStr.equals(ShiroConstants.CURRENT_USERNAME))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException
|
||||
{
|
||||
Object removed = super.removeAttribute(sessionKey, attributeKey);
|
||||
if (removed != null)
|
||||
{
|
||||
OnlineSession s = (OnlineSession) doGetSession(sessionKey);
|
||||
s.markAttributeChanged();
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证session是否有效 用于删除过期session
|
||||
*/
|
||||
@Override
|
||||
public void validateSessions()
|
||||
{
|
||||
if (log.isInfoEnabled())
|
||||
{
|
||||
log.info("invalidation sessions...");
|
||||
}
|
||||
|
||||
int invalidCount = 0;
|
||||
|
||||
int timeout = (int) this.getGlobalSessionTimeout();
|
||||
Date expiredDate = DateUtils.addMilliseconds(new Date(), 0 - timeout);
|
||||
SysUserOnlineServiceImpl userOnlineService = SpringUtils.getBean(SysUserOnlineServiceImpl.class);
|
||||
List<SysUserOnline> userOnlineList = userOnlineService.selectOnlineByExpired(expiredDate);
|
||||
// 批量过期删除
|
||||
List<String> needOfflineIdList = new ArrayList<String>();
|
||||
for (SysUserOnline userOnline : userOnlineList)
|
||||
{
|
||||
try
|
||||
{
|
||||
SessionKey key = new DefaultSessionKey(userOnline.getSessionId());
|
||||
Session session = retrieveSession(key);
|
||||
if (session != null)
|
||||
{
|
||||
throw new InvalidSessionException();
|
||||
}
|
||||
}
|
||||
catch (InvalidSessionException e)
|
||||
{
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
boolean expired = (e instanceof ExpiredSessionException);
|
||||
String msg = "Invalidated session with id [" + userOnline.getSessionId() + "]"
|
||||
+ (expired ? " (expired)" : " (stopped)");
|
||||
log.debug(msg);
|
||||
}
|
||||
invalidCount++;
|
||||
needOfflineIdList.add(userOnline.getSessionId());
|
||||
}
|
||||
|
||||
}
|
||||
if (needOfflineIdList.size() > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
userOnlineService.batchDeleteOnline(needOfflineIdList);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("batch delete db session error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (log.isInfoEnabled())
|
||||
{
|
||||
String msg = "Finished invalidation session.";
|
||||
if (invalidCount > 0)
|
||||
{
|
||||
msg += " [" + invalidCount + "] sessions were stopped.";
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += " No sessions were stopped.";
|
||||
}
|
||||
log.info(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Session> getActiveSessions()
|
||||
{
|
||||
throw new UnsupportedOperationException("getActiveSessions method not supported");
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package com.ruoyi.framework.shiro.web.session;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.shiro.session.mgt.DefaultSessionManager;
|
||||
import org.apache.shiro.session.mgt.SessionValidationScheduler;
|
||||
import org.apache.shiro.session.mgt.ValidatingSessionManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 自定义任务调度器完成
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SpringSessionValidationScheduler implements SessionValidationScheduler
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(SpringSessionValidationScheduler.class);
|
||||
|
||||
public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
|
||||
|
||||
/**
|
||||
* 定时器,用于处理超时的挂起请求,也用于连接断开时的重连。
|
||||
*/
|
||||
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
private volatile boolean enabled = false;
|
||||
|
||||
/**
|
||||
* The session manager used to validate sessions.
|
||||
*/
|
||||
private ValidatingSessionManager sessionManager;
|
||||
|
||||
/**
|
||||
* The session validation interval in milliseconds.
|
||||
*/
|
||||
private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public SpringSessionValidationScheduler()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that specifies the session manager that should be used for validating sessions.
|
||||
*
|
||||
* @param sessionManager the <tt>SessionManager</tt> that should be used to validate sessions.
|
||||
*/
|
||||
public SpringSessionValidationScheduler(ValidatingSessionManager sessionManager)
|
||||
{
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
public void setSessionManager(ValidatingSessionManager sessionManager)
|
||||
{
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies how frequently (in milliseconds) this Scheduler will call the
|
||||
* {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions()
|
||||
* ValidatingSessionManager#validateSessions()} method.
|
||||
*
|
||||
* <p>
|
||||
* Unless this method is called, the default value is {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}.
|
||||
*
|
||||
* @param sessionValidationInterval
|
||||
*/
|
||||
public void setSessionValidationInterval(long sessionValidationInterval)
|
||||
{
|
||||
this.sessionValidationInterval = sessionValidationInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts session validation by creating a spring PeriodicTrigger.
|
||||
*/
|
||||
@Override
|
||||
public void enableSessionValidation()
|
||||
{
|
||||
|
||||
enabled = true;
|
||||
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
log.debug("Scheduling session validation job using Spring Scheduler with "
|
||||
+ "session validation interval of [" + sessionValidationInterval + "]ms...");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
executorService.scheduleAtFixedRate(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
sessionManager.validateSessions();
|
||||
}
|
||||
}
|
||||
}, 1000, sessionValidationInterval, TimeUnit.MILLISECONDS);
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
log.debug("Session validation job successfully scheduled with Spring Scheduler.");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (log.isErrorEnabled())
|
||||
{
|
||||
log.error("Error starting the Spring Scheduler session validation job. Session validation may not occur.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableSessionValidation()
|
||||
{
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
log.debug("Stopping Spring Scheduler session validation job...");
|
||||
}
|
||||
|
||||
this.enabled = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
package com.ruoyi.framework.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.apache.tomcat.util.http.fileupload.FileUploadBase.FileSizeLimitExceededException;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.ruoyi.common.config.Global;
|
||||
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
|
||||
import com.ruoyi.common.utils.Md5Utils;
|
||||
|
||||
/**
|
||||
* 文件上传工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class FileUploadUtils
|
||||
{
|
||||
/**
|
||||
* 默认大小 50M
|
||||
*/
|
||||
public static final long DEFAULT_MAX_SIZE = 52428800;
|
||||
|
||||
/**
|
||||
* 默认上传的地址
|
||||
*/
|
||||
private static String defaultBaseDir = Global.getProfile();
|
||||
|
||||
/**
|
||||
* 默认的文件名最大长度
|
||||
*/
|
||||
public static final int DEFAULT_FILE_NAME_LENGTH = 200;
|
||||
|
||||
/**
|
||||
* 默认文件类型jpg
|
||||
*/
|
||||
public static final String IMAGE_JPG_EXTENSION = ".jpg";
|
||||
|
||||
private static int counter = 0;
|
||||
|
||||
public static void setDefaultBaseDir(String defaultBaseDir)
|
||||
{
|
||||
FileUploadUtils.defaultBaseDir = defaultBaseDir;
|
||||
}
|
||||
|
||||
public static String getDefaultBaseDir()
|
||||
{
|
||||
return defaultBaseDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以默认配置进行文件上传
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return 文件名称
|
||||
* @throws Exception
|
||||
*/
|
||||
public static final String upload(MultipartFile file) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return upload(getDefaultBaseDir(), file, FileUploadUtils.IMAGE_JPG_EXTENSION);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件路径上传
|
||||
*
|
||||
* @param baseDir 相对应用的基目录
|
||||
* @param file 上传的文件
|
||||
* @return 文件名称
|
||||
* @throws IOException
|
||||
*/
|
||||
public static final String upload(String baseDir, MultipartFile file) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return upload(baseDir, file, FileUploadUtils.IMAGE_JPG_EXTENSION);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param baseDir 相对应用的基目录
|
||||
* @param file 上传的文件
|
||||
* @param needDatePathAndRandomName 是否需要日期目录和随机文件名前缀
|
||||
* @param extension 上传文件类型
|
||||
* @return 返回上传成功的文件名
|
||||
* @throws FileSizeLimitExceededException 如果超出最大大小
|
||||
* @throws FileNameLengthLimitExceededException 文件名太长
|
||||
* @throws IOException 比如读写文件出错时
|
||||
*/
|
||||
public static final String upload(String baseDir, MultipartFile file, String extension)
|
||||
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException
|
||||
{
|
||||
|
||||
int fileNamelength = file.getOriginalFilename().length();
|
||||
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
|
||||
{
|
||||
throw new FileNameLengthLimitExceededException(file.getOriginalFilename(), fileNamelength,
|
||||
FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
|
||||
}
|
||||
|
||||
assertAllowed(file);
|
||||
|
||||
String fileName = encodingFilename(file.getOriginalFilename(), extension);
|
||||
|
||||
File desc = getAbsoluteFile(baseDir, baseDir + fileName);
|
||||
file.transferTo(desc);
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private static final File getAbsoluteFile(String uploadDir, String filename) throws IOException
|
||||
{
|
||||
File desc = new File(File.separator + filename);
|
||||
|
||||
if (!desc.getParentFile().exists())
|
||||
{
|
||||
desc.getParentFile().mkdirs();
|
||||
}
|
||||
if (!desc.exists())
|
||||
{
|
||||
desc.createNewFile();
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码文件名
|
||||
*/
|
||||
private static final String encodingFilename(String filename, String extension)
|
||||
{
|
||||
filename = filename.replace("_", " ");
|
||||
filename = Md5Utils.hash(filename + System.nanoTime() + counter++) + extension;
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件大小校验
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return
|
||||
* @throws FileSizeLimitExceededException 如果超出最大大小
|
||||
*/
|
||||
public static final void assertAllowed(MultipartFile file) throws FileSizeLimitExceededException
|
||||
{
|
||||
long size = file.getSize();
|
||||
if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
|
||||
{
|
||||
throw new FileSizeLimitExceededException("not allowed upload upload", size, DEFAULT_MAX_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
package com.ruoyi.framework.util;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.ruoyi.common.json.JSON;
|
||||
import com.ruoyi.common.utils.IpUtils;
|
||||
|
||||
/**
|
||||
* 处理并记录日志文件
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class LogUtils
|
||||
{
|
||||
public static final Logger ERROR_LOG = LoggerFactory.getLogger("sys-error");
|
||||
public static final Logger ACCESS_LOG = LoggerFactory.getLogger("sys-access");
|
||||
|
||||
/**
|
||||
* 记录访问日志 [username][jsessionid][ip][accept][UserAgent][url][params][Referer]
|
||||
*
|
||||
* @param request
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void logAccess(HttpServletRequest request) throws Exception
|
||||
{
|
||||
String username = getUsername();
|
||||
String jsessionId = request.getRequestedSessionId();
|
||||
String ip = IpUtils.getIpAddr(request);
|
||||
String accept = request.getHeader("accept");
|
||||
String userAgent = request.getHeader("User-Agent");
|
||||
String url = request.getRequestURI();
|
||||
String params = getParams(request);
|
||||
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append(getBlock(username));
|
||||
s.append(getBlock(jsessionId));
|
||||
s.append(getBlock(ip));
|
||||
s.append(getBlock(accept));
|
||||
s.append(getBlock(userAgent));
|
||||
s.append(getBlock(url));
|
||||
s.append(getBlock(params));
|
||||
s.append(getBlock(request.getHeader("Referer")));
|
||||
getAccessLog().info(s.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录异常错误 格式 [exception]
|
||||
*
|
||||
* @param message
|
||||
* @param e
|
||||
*/
|
||||
public static void logError(String message, Throwable e)
|
||||
{
|
||||
String username = getUsername();
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append(getBlock("exception"));
|
||||
s.append(getBlock(username));
|
||||
s.append(getBlock(message));
|
||||
ERROR_LOG.error(s.toString(), e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录页面错误 错误日志记录 [page/eception][username][statusCode][errorMessage][servletName][uri][exceptionName][ip][exception]
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public static void logPageError(HttpServletRequest request)
|
||||
{
|
||||
String username = getUsername();
|
||||
|
||||
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
|
||||
String message = (String) request.getAttribute("javax.servlet.error.message");
|
||||
String uri = (String) request.getAttribute("javax.servlet.error.request_uri");
|
||||
Throwable t = (Throwable) request.getAttribute("javax.servlet.error.exception");
|
||||
|
||||
if (statusCode == null)
|
||||
{
|
||||
statusCode = 0;
|
||||
}
|
||||
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append(getBlock(t == null ? "page" : "exception"));
|
||||
s.append(getBlock(username));
|
||||
s.append(getBlock(statusCode));
|
||||
s.append(getBlock(message));
|
||||
s.append(getBlock(IpUtils.getIpAddr(request)));
|
||||
|
||||
s.append(getBlock(uri));
|
||||
s.append(getBlock(request.getHeader("Referer")));
|
||||
StringWriter sw = new StringWriter();
|
||||
|
||||
while (t != null)
|
||||
{
|
||||
t.printStackTrace(new PrintWriter(sw));
|
||||
t = t.getCause();
|
||||
}
|
||||
s.append(getBlock(sw.toString()));
|
||||
getErrorLog().error(s.toString());
|
||||
|
||||
}
|
||||
|
||||
public static String getBlock(Object msg)
|
||||
{
|
||||
if (msg == null)
|
||||
{
|
||||
msg = "";
|
||||
}
|
||||
return "[" + msg.toString() + "]";
|
||||
}
|
||||
|
||||
protected static String getParams(HttpServletRequest request) throws Exception
|
||||
{
|
||||
Map<String, String[]> params = request.getParameterMap();
|
||||
return JSON.marshal(params);
|
||||
}
|
||||
|
||||
protected static String getUsername()
|
||||
{
|
||||
return (String) SecurityUtils.getSubject().getPrincipal();
|
||||
}
|
||||
|
||||
public static Logger getAccessLog()
|
||||
{
|
||||
return ACCESS_LOG;
|
||||
}
|
||||
|
||||
public static Logger getErrorLog()
|
||||
{
|
||||
return ERROR_LOG;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.ruoyi.framework.util;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
|
||||
/**
|
||||
* 获取i18n资源文件
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class MessageUtils
|
||||
{
|
||||
/**
|
||||
* 根据消息键和参数 获取消息 委托给spring messageSource
|
||||
*
|
||||
* @param code 消息键
|
||||
* @param args 参数
|
||||
* @return
|
||||
*/
|
||||
public static String message(String code, Object... args)
|
||||
{
|
||||
MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
|
||||
return messageSource.getMessage(code, args, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.ruoyi.framework.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.ruoyi.common.constant.PermissionConstants;
|
||||
|
||||
/**
|
||||
* permission 工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class PermissionUtils
|
||||
{
|
||||
/**
|
||||
* 权限错误消息提醒
|
||||
*
|
||||
* @param errorMsg 错误信息
|
||||
* @return
|
||||
*/
|
||||
public static String getMsg(String permissionsStr)
|
||||
{
|
||||
String permission = StringUtils.substringBetween(permissionsStr, "[", "]");
|
||||
String msg = MessageUtils.message("no.view.permission", permission);
|
||||
if (StringUtils.endsWithIgnoreCase(permission, PermissionConstants.ADD_PERMISSION))
|
||||
{
|
||||
msg = MessageUtils.message("no.create.permission", permission);
|
||||
}
|
||||
else if (StringUtils.endsWithIgnoreCase(permission, PermissionConstants.EDIT_PERMISSION))
|
||||
{
|
||||
msg = MessageUtils.message("no.update.permission", permission);
|
||||
}
|
||||
else if (StringUtils.endsWithIgnoreCase(permission, PermissionConstants.REMOVE_PERMISSION))
|
||||
{
|
||||
msg = MessageUtils.message("no.delete.permission", permission);
|
||||
}
|
||||
else if (StringUtils.endsWithIgnoreCase(permission, PermissionConstants.EXPORT_PERMISSION))
|
||||
{
|
||||
msg = MessageUtils.message("no.export.permission", permission);
|
||||
}
|
||||
else if (StringUtils.endsWithAny(permission,
|
||||
new String[] { PermissionConstants.VIEW_PERMISSION, PermissionConstants.LIST_PERMISSION }))
|
||||
{
|
||||
msg = MessageUtils.message("no.view.permission", permission);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
package com.ruoyi.framework.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import com.ruoyi.common.support.Convert;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 客户端工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ServletUtils
|
||||
{
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name)
|
||||
{
|
||||
return getRequest().getParameter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name, String defaultValue)
|
||||
{
|
||||
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name)
|
||||
{
|
||||
return Convert.toInt(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name, Integer defaultValue)
|
||||
{
|
||||
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取request
|
||||
*/
|
||||
public static HttpServletRequest getRequest()
|
||||
{
|
||||
return getRequestAttributes().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取response
|
||||
*/
|
||||
public static HttpServletResponse getResponse()
|
||||
{
|
||||
return getRequestAttributes().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取session
|
||||
*/
|
||||
public static HttpSession getSession()
|
||||
{
|
||||
return getRequest().getSession();
|
||||
}
|
||||
|
||||
public static ServletRequestAttributes getRequestAttributes()
|
||||
{
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes) attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串渲染到客户端
|
||||
*
|
||||
* @param response 渲染对象
|
||||
* @param string 待渲染的字符串
|
||||
* @return null
|
||||
*/
|
||||
public static String renderString(HttpServletResponse response, String string)
|
||||
{
|
||||
try
|
||||
{
|
||||
response.setContentType("application/json");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.getWriter().print(string);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是Ajax异步请求
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public static boolean isAjaxRequest(HttpServletRequest request)
|
||||
{
|
||||
|
||||
String accept = request.getHeader("accept");
|
||||
if (accept != null && accept.indexOf("application/json") != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String xRequestedWith = request.getHeader("X-Requested-With");
|
||||
if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String ajax = request.getParameter("__ajax");
|
||||
if (StringUtils.inStringIgnoreCase(ajax, "json", "xml"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.ruoyi.framework.util;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
|
||||
import org.apache.shiro.mgt.RealmSecurityManager;
|
||||
import org.apache.shiro.session.Session;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.bean.BeanUtils;
|
||||
import com.ruoyi.framework.shiro.realm.UserRealm;
|
||||
import com.ruoyi.system.domain.SysUser;
|
||||
|
||||
/**
|
||||
* shiro 工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ShiroUtils
|
||||
{
|
||||
public static Subject getSubjct()
|
||||
{
|
||||
return SecurityUtils.getSubject();
|
||||
}
|
||||
|
||||
public static Session getSession()
|
||||
{
|
||||
return SecurityUtils.getSubject().getSession();
|
||||
}
|
||||
|
||||
public static void logout()
|
||||
{
|
||||
getSubjct().logout();
|
||||
}
|
||||
|
||||
public static SysUser getUser()
|
||||
{
|
||||
SysUser user = null;
|
||||
Object obj = getSubjct().getPrincipal();
|
||||
if (StringUtils.isNotNull(obj))
|
||||
{
|
||||
user = new SysUser();
|
||||
BeanUtils.copyBeanProp(user, obj);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public static void setUser(SysUser user)
|
||||
{
|
||||
Subject subject = getSubjct();
|
||||
PrincipalCollection principalCollection = subject.getPrincipals();
|
||||
String realmName = principalCollection.getRealmNames().iterator().next();
|
||||
PrincipalCollection newPrincipalCollection = new SimplePrincipalCollection(user, realmName);
|
||||
// 重新加载Principal
|
||||
subject.runAs(newPrincipalCollection);
|
||||
}
|
||||
|
||||
public static void clearCachedAuthorizationInfo()
|
||||
{
|
||||
RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager();
|
||||
UserRealm realm = (UserRealm) rsm.getRealms().iterator().next();
|
||||
realm.clearCachedAuthorizationInfo();
|
||||
}
|
||||
|
||||
public static Long getUserId()
|
||||
{
|
||||
return getUser().getUserId().longValue();
|
||||
}
|
||||
|
||||
public static String getLoginName()
|
||||
{
|
||||
return getUser().getLoginName();
|
||||
}
|
||||
|
||||
public static String getIp()
|
||||
{
|
||||
return getSubjct().getSession().getHost();
|
||||
}
|
||||
|
||||
public static String getSessionId()
|
||||
{
|
||||
return String.valueOf(getSubjct().getSession().getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机盐
|
||||
*/
|
||||
public static String randomSalt()
|
||||
{
|
||||
// 一个Byte占两个字节,此处生成的3字节,字符串长度为6
|
||||
SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator();
|
||||
String hex = secureRandom.nextBytes(3).toHex();
|
||||
return hex;
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package com.ruoyi.framework.util;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* spring工具类 方便在非spring管理环境中获取bean
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public final class SpringUtils implements BeanFactoryPostProcessor
|
||||
{
|
||||
/** Spring应用上下文环境 */
|
||||
private static ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
|
||||
{
|
||||
SpringUtils.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param name
|
||||
* @return Object 一个以所给名字注册的bean的实例
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(String name) throws BeansException
|
||||
{
|
||||
return (T) beanFactory.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型为requiredType的对象
|
||||
*
|
||||
* @param clz
|
||||
* @return
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clz) throws BeansException
|
||||
{
|
||||
T result = (T) beanFactory.getBean(clz);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean containsBean(String name)
|
||||
{
|
||||
return beanFactory.containsBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.isSingleton(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return Class 注册对象的类型
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.getType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.getAliases(name);
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package com.ruoyi.framework.web.exception;
|
||||
|
||||
import org.apache.shiro.authz.AuthorizationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import com.ruoyi.common.base.AjaxResult;
|
||||
import com.ruoyi.common.exception.DemoModeException;
|
||||
import com.ruoyi.framework.util.PermissionUtils;
|
||||
|
||||
/**
|
||||
* 自定义异常处理器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class DefaultExceptionHandler
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(DefaultExceptionHandler.class);
|
||||
|
||||
/**
|
||||
* 权限校验失败
|
||||
*/
|
||||
@ExceptionHandler(AuthorizationException.class)
|
||||
public AjaxResult handleAuthorizationException(AuthorizationException e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
return AjaxResult.error(PermissionUtils.getMsg(e.getMessage()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求方式不支持
|
||||
*/
|
||||
@ExceptionHandler({ HttpRequestMethodNotSupportedException.class })
|
||||
public AjaxResult handleException(HttpRequestMethodNotSupportedException e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
return AjaxResult.error("不支持' " + e.getMethod() + "'请求");
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截未知的运行时异常
|
||||
*/
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public AjaxResult notFount(RuntimeException e)
|
||||
{
|
||||
log.error("运行时异常:", e);
|
||||
return AjaxResult.error("运行时异常:" + e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统异常
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public AjaxResult handleException(Exception e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
return AjaxResult.error("服务器错误,请联系管理员");
|
||||
}
|
||||
|
||||
/**
|
||||
* 演示模式异常
|
||||
*/
|
||||
@ExceptionHandler(DemoModeException.class)
|
||||
public AjaxResult demoModeException(DemoModeException e)
|
||||
{
|
||||
return AjaxResult.error("演示模式,不允许操作");
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package com.ruoyi.framework.web.exception.base;
|
||||
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.util.MessageUtils;
|
||||
|
||||
/**
|
||||
* 基础异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class BaseException extends RuntimeException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 所属模块
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 错误码对应的参数
|
||||
*/
|
||||
private Object[] args;
|
||||
|
||||
/**
|
||||
* 错误消息
|
||||
*/
|
||||
private String defaultMessage;
|
||||
|
||||
public BaseException(String module, String code, Object[] args, String defaultMessage)
|
||||
{
|
||||
this.module = module;
|
||||
this.code = code;
|
||||
this.args = args;
|
||||
this.defaultMessage = defaultMessage;
|
||||
}
|
||||
|
||||
public BaseException(String module, String code, Object[] args)
|
||||
{
|
||||
this(module, code, args, null);
|
||||
}
|
||||
|
||||
public BaseException(String module, String defaultMessage)
|
||||
{
|
||||
this(module, null, null, defaultMessage);
|
||||
}
|
||||
|
||||
public BaseException(String code, Object[] args)
|
||||
{
|
||||
this(null, code, args, null);
|
||||
}
|
||||
|
||||
public BaseException(String defaultMessage)
|
||||
{
|
||||
this(null, null, null, defaultMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
String message = null;
|
||||
if (!StringUtils.isEmpty(code))
|
||||
{
|
||||
message = MessageUtils.message(code, args);
|
||||
}
|
||||
if (message == null)
|
||||
{
|
||||
message = defaultMessage;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getModule()
|
||||
{
|
||||
return module;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public Object[] getArgs()
|
||||
{
|
||||
return args;
|
||||
}
|
||||
|
||||
public String getDefaultMessage()
|
||||
{
|
||||
return defaultMessage;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.framework.web.exception.user;
|
||||
|
||||
/**
|
||||
* 验证码错误异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class CaptchaException extends UserException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CaptchaException()
|
||||
{
|
||||
super("user.jcaptcha.error", null);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.framework.web.exception.user;
|
||||
|
||||
/**
|
||||
* 角色锁定异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class RoleBlockedException extends UserException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public RoleBlockedException(String reason)
|
||||
{
|
||||
super("role.blocked", new Object[] { reason });
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.framework.web.exception.user;
|
||||
|
||||
/**
|
||||
* 用户锁定异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserBlockedException extends UserException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserBlockedException(String reason)
|
||||
{
|
||||
super("user.blocked", new Object[] { reason });
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.framework.web.exception.user;
|
||||
|
||||
/**
|
||||
* 用户账号已被删除
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserDeleteException extends UserException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserDeleteException()
|
||||
{
|
||||
super("user.password.delete", null);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.ruoyi.framework.web.exception.user;
|
||||
|
||||
import com.ruoyi.framework.web.exception.base.BaseException;
|
||||
|
||||
/**
|
||||
* 用户信息异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserException extends BaseException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserException(String code, Object[] args)
|
||||
{
|
||||
super("user", code, args, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.framework.web.exception.user;
|
||||
|
||||
/**
|
||||
* 用户不存在异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserNotExistsException extends UserException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserNotExistsException()
|
||||
{
|
||||
super("user.not.exists", null);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.framework.web.exception.user;
|
||||
|
||||
/**
|
||||
* 用户密码不正确或不符合规范异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserPasswordNotMatchException extends UserException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserPasswordNotMatchException()
|
||||
{
|
||||
super("user.password.not.match", null);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.framework.web.exception.user;
|
||||
|
||||
/**
|
||||
* 用户错误记数异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserPasswordRetryLimitCountException extends UserException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserPasswordRetryLimitCountException(int retryLimitCount)
|
||||
{
|
||||
super("user.password.retry.limit.count", new Object[] { retryLimitCount });
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.framework.web.exception.user;
|
||||
|
||||
/**
|
||||
* 用户错误最大次数异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserPasswordRetryLimitExceedException extends UserException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserPasswordRetryLimitExceedException(int retryLimitCount)
|
||||
{
|
||||
super("user.password.retry.limit.exceed", new Object[] { retryLimitCount });
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.ruoyi.framework.web.page;
|
||||
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 分页数据
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class PageDomain
|
||||
{
|
||||
/** 当前记录起始索引 */
|
||||
private Integer pageNum;
|
||||
|
||||
/** 每页显示记录数 */
|
||||
private Integer pageSize;
|
||||
|
||||
/** 排序列 */
|
||||
private String orderByColumn;
|
||||
/** 排序的方向 "desc" 或者 "asc". */
|
||||
|
||||
private String isAsc;
|
||||
|
||||
public String getOrderBy()
|
||||
{
|
||||
if (StringUtils.isEmpty(orderByColumn))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
|
||||
}
|
||||
|
||||
public Integer getPageNum()
|
||||
{
|
||||
return pageNum;
|
||||
}
|
||||
|
||||
public void setPageNum(Integer pageNum)
|
||||
{
|
||||
this.pageNum = pageNum;
|
||||
}
|
||||
|
||||
public Integer getPageSize()
|
||||
{
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(Integer pageSize)
|
||||
{
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public String getOrderByColumn()
|
||||
{
|
||||
return orderByColumn;
|
||||
}
|
||||
|
||||
public void setOrderByColumn(String orderByColumn)
|
||||
{
|
||||
this.orderByColumn = orderByColumn;
|
||||
}
|
||||
|
||||
public String getIsAsc()
|
||||
{
|
||||
return isAsc;
|
||||
}
|
||||
|
||||
public void setIsAsc(String isAsc)
|
||||
{
|
||||
this.isAsc = isAsc;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.ruoyi.framework.web.page;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表格分页数据对象
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class TableDataInfo implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
/** 总记录数 */
|
||||
private long total;
|
||||
/** 列表数据 */
|
||||
private List<?> rows;
|
||||
/** 消息状态码 */
|
||||
private int code;
|
||||
|
||||
/**
|
||||
* 表格数据对象
|
||||
*/
|
||||
public TableDataInfo()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*
|
||||
* @param list 列表数据
|
||||
* @param total 总记录数
|
||||
*/
|
||||
public TableDataInfo(List<?> list, int total)
|
||||
{
|
||||
this.rows = list;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public long getTotal()
|
||||
{
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long total)
|
||||
{
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public List<?> getRows()
|
||||
{
|
||||
return rows;
|
||||
}
|
||||
|
||||
public void setRows(List<?> rows)
|
||||
{
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
public int getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.ruoyi.framework.web.page;
|
||||
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.framework.util.ServletUtils;
|
||||
|
||||
/**
|
||||
* 表格数据处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class TableSupport
|
||||
{
|
||||
/**
|
||||
* 封装分页对象
|
||||
*/
|
||||
public static PageDomain getPageDomain()
|
||||
{
|
||||
PageDomain pageDomain = new PageDomain();
|
||||
pageDomain.setPageNum(ServletUtils.getParameterToInt(Constants.PAGE_NUM));
|
||||
pageDomain.setPageSize(ServletUtils.getParameterToInt(Constants.PAGE_SIZE));
|
||||
pageDomain.setOrderByColumn(ServletUtils.getParameter(Constants.ORDER_BY_COLUMN));
|
||||
pageDomain.setIsAsc(ServletUtils.getParameter(Constants.IS_ASC));
|
||||
return pageDomain;
|
||||
}
|
||||
|
||||
public static PageDomain buildPageRequest()
|
||||
{
|
||||
return getPageDomain();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.ruoyi.framework.web.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.system.service.ISysConfigService;
|
||||
|
||||
/**
|
||||
* RuoYi首创 html调用 thymeleaf 实现参数管理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service("config")
|
||||
public class ConfigService
|
||||
{
|
||||
@Autowired
|
||||
private ISysConfigService configService;
|
||||
|
||||
/**
|
||||
* 根据键名查询参数配置信息
|
||||
*
|
||||
* @param configName 参数名称
|
||||
* @return 参数键值
|
||||
*/
|
||||
public String getKey(String configKey)
|
||||
{
|
||||
return configService.selectConfigByKey(configKey);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.ruoyi.framework.web.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.system.domain.SysDictData;
|
||||
import com.ruoyi.system.service.ISysDictDataService;
|
||||
|
||||
/**
|
||||
* RuoYi首创 html调用 thymeleaf 实现字典读取
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service("dict")
|
||||
public class DictService
|
||||
{
|
||||
@Autowired
|
||||
private ISysDictDataService dictDataService;
|
||||
|
||||
/**
|
||||
* 根据字典类型查询字典数据信息
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @return 参数键值
|
||||
*/
|
||||
public List<SysDictData> getType(String dictType)
|
||||
{
|
||||
return dictDataService.selectDictDataByType(dictType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典键值查询字典数据信息
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictValue 字典键值
|
||||
* @return 字典标签
|
||||
*/
|
||||
public String getLabel(String dictType, String dictValue)
|
||||
{
|
||||
return dictDataService.selectDictLabel(dictType, dictValue);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.ruoyi.framework.web.service;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* RuoYi首创 js调用 thymeleaf 实现按钮权限可见性
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service("permission")
|
||||
public class PermissionService
|
||||
{
|
||||
public String hasPermi(String permission)
|
||||
{
|
||||
return isPermittedOperator(permission) ? "" : "hidden";
|
||||
}
|
||||
|
||||
private boolean isPermittedOperator(String permission)
|
||||
{
|
||||
return SecurityUtils.getSubject().isPermitted(permission);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user