2、framework包css
--aspectj包html
DataScopeAspect.java-------數據過濾處理
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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---------多源數據處理
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------操做日誌記錄處理
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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; } }
--config包
CaptchaConfig.java-------驗證碼配置
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------配置多個數據源
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource); return new DynamicDataSource(masterDataSource, targetDataSources); } }
FilterConfig.java---------過濾器配置
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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; } }
GenConfig.java----------讀取代碼生成相關配置
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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---------資源文件配置加載
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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----------驗證碼文本生成器
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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---------通用配置
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-----------權限配置加載
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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鏈接約束配置,即過濾鏈的定義 LinkedHashMap<String, String> filterChainDefinitionMap = 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<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; } }
-----datasource包java
DynamicDataSource.java----------動態數據源
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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(); } }
DynamicDataSourceContextHolder.java-----------動態數據源切換處理
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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(); } }
--manager包web
-----factory包ajax
AsyncFactory.java--------異步工廠(產生任務時用到)
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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---------異步任務管理器
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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包spring
-------realm包sql
UserRealm.java----------自定義realm處理登錄權限
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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()); } }
-------service包數據庫
LoginService.java---------登錄校驗方法
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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----------登錄密碼方法
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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")); } }
-------session包apache
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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------------在線用戶會話屬性
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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對話
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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包json
-----------filter包
--------------captcha包
CaptchaValidateFilter.java----------驗證碼過濾器
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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--------自定義訪問控制
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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數據到數據庫
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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--------退出過濾器
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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------標識會話修改
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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"); } }
SpringSessionValidationScheduler.java------自定義任務調度器
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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; } }
--web包
------exception包
--------base包
BaseException.java------基礎異常
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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------驗證碼錯誤異常類
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------角色鎖定異常類
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------用戶鎖定異常類
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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--------用戶帳號刪除異常
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------用戶信息異常
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------用戶不存在異常類
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------用戶密碼不正確或不規範異常
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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--------用戶錯誤記錄異常
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------用戶錯誤最大次數異常
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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---------自定義異常處理器
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------分頁數據
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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------表格分頁數據對象
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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-------表格數據處理
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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 實現參數管理
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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 實現字典讀取
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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); } }
PermissionService.java------------js調用 thymeleaf 實現按鈕權限可見性
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
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); } }