二、framework包
--aspectj包
DataScopeAspect.java-------数据过滤处理
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; } }
DataSourceAspect.java---------多源数据处理
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(); } } }
LogAspect.java-------操作日志记录处理
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 { Mapmap = 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; } }
--config包
CaptchaConfig.java-------验证码配置
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; } }
DruidConfig.java-------配置多个数据源
package com.ruoyi.framework.config; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import com.ruoyi.common.enums.DataSourceType; import com.ruoyi.framework.datasource.DynamicDataSource; 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 javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 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
FilterConfig.java---------过滤器配置
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); MapinitParameters = new HashMap (); initParameters.put("excludes", excludes); initParameters.put("enabled", enabled); registration.setInitParameters(initParameters); return registration; } }
GenConfig.java----------读取代码生成相关配置
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; } }
I18nConfig.java---------资源文件配置加载
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()); } }
KaptchaTextCreator.java----------验证码文本生成器
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(); } }
ResourcesConfig.java---------通用配置
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/"); } }
ShiroConfig.java-----------权限配置加载
package com.ruoyi.framework.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; 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 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 javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.Map; /** * 权限配置加载 * * @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连接约束配置,即过滤链的定义 LinkedHashMapfilterChainDefinitionMap = new LinkedHashMap<>(); // 对静态资源设置匿名访问 filterChainDefinitionMap.put("/favicon.ico**", "anon"); // uflo工作流不拦截 filterChainDefinitionMap.put("/uflo/**", "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 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; } }
-----datasource包
DynamicDataSource.java----------动态数据源
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
DynamicDataSourceContextHolder.java-----------动态数据源切换处理
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 ThreadLocalCONTEXT_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(); } }
--manager包
-----factory包
AsyncFactory.java--------异步工厂(产生任务时用到)
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); } }; } }
---------------------
AsyncManager.java---------异步任务管理器
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); } }
--shiro包
-------realm包
UserRealm.java----------自定义realm处理登陆权限
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(); // 角色列表 Setroles = new HashSet (); // 功能列表 Set menus = new HashSet (); 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()); } }
-------service包
LoginService.java---------登陆校验方法
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); } }
PasswordService.java----------登陆密码方法
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 CacheloginRecordCache; @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")); } }
-------session包
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); } }
OnlineSessionDAO.java------------在线用户会话属性
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())); } }
OnlineSessionFactory.java------------自定义session对话
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; } }
--------web包
-----------filter包
--------------captcha包
CaptchaValidateFilter.java----------验证码过滤器
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; } }
---------------online包
OnlineSessionFilter.java--------自定义访问控制
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); } }
---------------sysnc包
SyncOnlineSessionFilter.java-------同步session数据到数据库
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; } }
-----------------------
LogoutFilter.java--------退出过滤器
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); } }
-----------session包
OnlineWebSessionManager.java------标识会话修改
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); ListuserOnlineList = userOnlineService.selectOnlineByExpired(expiredDate); // 批量过期删除 List needOfflineIdList = new ArrayList (); 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 getActiveSessions() { throw new UnsupportedOperationException("getActiveSessions method not supported"); } }
SpringSessionValidationScheduler.java------自定义任务调度器
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 SessionManager 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. * ** 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; } }
--web包
------exception包
--------base包
BaseException.java------基础异常
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; } }
--------user包
CaptchaException.java------验证码错误异常类
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); } }
RoleBlockedException.java-------角色锁定异常类
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 }); } }
UserBlockedException.java-------用户锁定异常类
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 }); } }
UserDeleteException.java--------用户账号删除异常
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); } }
UserException.java-------用户信息异常
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); } }
UserNotExistsException.java-------用户不存在异常类
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); } }
UserPasswordNotMatchException.java-------用户密码不正确或不规范异常
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); } }
UserPasswordRetryLimitCountException.java--------用户错误记录异常
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 }); } }
UserPasswordRetryLimitExceedException.java-------用户错误最大次数异常
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 }); } }
-----------------
DefaultExceptionHandler.java---------自定义异常处理器
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("演示模式,不允许操作"); } }
------page包
PageDomain.java-------分页数据
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; } }
TableDataInfo.java------表格分页数据对象
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; } }
TableSupport.java-------表格数据处理
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(); } }
------service包
ConfigService.java------html调用 thymeleaf 实现参数管理
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); } }
DictService.java----------html调用 thymeleaf 实现字典读取
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 ListgetType(String dictType) { return dictDataService.selectDictDataByType(dictType); } /** * 根据字典类型和字典键值查询字典数据信息 * * @param dictType 字典类型 * @param dictValue 字典键值 * @return 字典标签 */ public String getLabel(String dictType, String dictValue) { return dictDataService.selectDictLabel(dictType, dictValue); } }
PermissionService.java------------js调用 thymeleaf 实现按钮权限可见性
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); } }