后台管理系統
版本更新
后續版本更新內容
鏈接入口:
- springboot + shiro之登錄人數限制、登錄判斷重定向、session時間設置:https://blog.51cto.com/wyait/2107423
- springboot + shiro 動態更新用戶信息:https://blog.51cto.com/wyait/2112200
- springboot + shiro 權限注解、統一異常處理、請求亂碼解決 :https://blog.51cto.com/wyait/2125708
- shiro並發登陸人數控制(超出登錄用戶最大配置數量,清理用戶)功能;
- 解決父子頁面判斷用戶未登錄之后,重定向到頁面中嵌套顯示登錄界面問題;
- 解決ajax請求,判斷用戶未登錄之后,重定向到登錄頁面問題;
- 解決完成了功能1,導致的session有效時間沖突問題等。
其他時間的版本更新,詳見本文末尾或git項目更新日志!
下期版本更新內容
- 新建wyait-admin單數據源配置項目;
- redis版本,實現用戶在線數量控制功能等;
-
使用redis記錄驗證碼;
業務場景
- spring boot + mybatis后台管理系統框架;
- layUI前端界面;
- shiro權限控制,ehCache緩存;
開發背景
maven :3.3.3
JDK : 1.8
Intellij IDEA : 2017.2.5 開發工具
spring boot :1.5.9.RELEASE
mybatis 3.4.5 :dao層框架
pageHelper : 5.1.2
httpClient : 4.5.3
layui 2.2.3 :前端框架
shiro 1.4.0 :權限控制框架
druid 1.1.5 :druid連接池,監控數據庫性能,記錄SQL執行日志
thymeleaf :2.1.4.RELEASE,thymeleaf前端html頁面模版
log4j2 2.7 :日志框架
EHCache : 2.5.0
ztree : 3.5.31
項目框架
spring boot + mybatis + shiro + layui + ehcache
項目源碼:(包含數據庫源碼)
github源碼: https://github.com/wyait/manage.git
碼雲:https://gitee.com/wyait/manage.git
基礎框架
spring boot + mybatis的整合,參考博客:
https://blog.51cto.com/wyait/1969626
spring boot之靜態資源路徑配置
靜態資源路徑是指系統可以直接訪問的路徑,且路徑下的所有文件均可被用戶直接讀取。
在Springboot中默認的靜態資源路徑有:classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,從這里可以看出這里的靜態資源路徑都是在classpath中(也就是在項目路徑下指定的這幾個文件夾)
試想這樣一種情況:一個網站有文件上傳文件的功能,如果被上傳的文件放在上述的那些文件夾中會有怎樣的后果?
網站數據與程序代碼不能有效分離;
當項目被打包成一個.jar文件部署時,再將上傳的文件放到這個.jar文件中是有多么低的效率; 網站數據的備份將會很痛苦。
此時可能最佳的解決辦法是將靜態資源路徑設置到磁盤的某個目錄。與應用程序分離。
在Springboot中可以直接在配置文件中覆蓋默認的靜態資源路徑的配置信息:
application.properties配置文件如下:
# 靜態資源路徑配置 wyait.picpath=D:/demo-images/ spring.mvc.static-path-pattern=/** spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${wyait.picpath}
注意wyait.picpath這個屬於自定義的屬性,指定了一個路徑,注意要以/結尾;
spring.mvc.static-path-pattern=/ 表示所有的訪問都經過靜態資源路徑;
spring.resources.static-locations 在這里配置靜態資源路徑,前面說了這里的配置是覆蓋默認配置,所以需要將默認的也加上否則static、public等這些路徑將不能被當作靜態資源路徑,在這個最末尾的file:${wyait.picpath} ==file:${wyait.picpath}==,
加 file :是因為指定的是一個具體的硬盤路徑,其他的使用classpath指的是系統環境變量。
問題
圖片或靜態資源直接放在wyait.picpath=D:/demo-images/目錄下,訪問:http://127.0.0.1:8077/0.jpg,會報錯:
[2018-04-08 22:05:32.095][http-nio-8077-exec-3][ERROR][org.apache.juli.logging.DirectJDKLog][181]:Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "0", template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause org.thymeleaf.exceptions.TemplateInputException: Error resolving template "0", template might not exist or might not be accessible by any of the configured Template Resolvers at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246) ~[thymeleaf-2.1.6.RELEASE.jar:2.1.6.RELEASE]
原因應該是在項目集成shiro時,shiro對contextPath/后面的第一層path訪問時,對標點“.”進行了截取,實際請求變成了:http://127.0.0.1:8077/0 , 交給dispatcherServlet處理,沒有找到匹配的view視圖“0”,就報錯。具體原因抽空跟蹤下源碼。
解決方案:
這個file靜態資源配置,在項目開發訪問時,需要在wyait.picpath=D:/demo-images/配置的目錄下,再加一層或一層以上的目錄。如圖: 
比如:保存圖片時,一般會根據年月日進行分目錄,實際圖片保存在D:/demo-images/201804/0.jpg目錄下;訪問的時候,直接:http://127.0.0.1:8077/2018/0.jpg,即可訪問到圖片。
添加一層或多層目錄之后,springboot會在靜態資源配置中依次找到匹配的目錄,然后加載靜態資源;
自定義靜態資源配置方法
自定義靜態資源配置方法,參考博客:https://blog.51cto.com/wyait/1971108 博客末尾處,提供了自定義靜態資源訪問方法,通過配置類設置對應的路徑進行靜態資源訪問。
總結
此配置解決了springboot+thymeleaf架構的獲取圖片(靜態資源)404的問題;之前的SpringMVC + jsp在讀取圖片的時候,本地或服務器在讀取用戶上傳的圖片時,需要配置nginx;spring boot在不更換域名的前提下,默認是根據application.xml文件的靜態資源路徑配置查找圖片等靜態資源;nginx配置是無效的,會導致圖片無法獲取(讀取404)。
所以如果要對圖片或其他靜態資源進行應用程序分離時,需要使用以上配置,覆蓋原springboot默認配置,另外,不需要額外配置nginx,也是一個優點。
整合layui
layui官網:http://www.layui.com
layui下載地址:https://github.com/sentsin/layui/
-
將下載的layui解壓后,復制到項目的static/目錄下:

- 在templates/目錄下,新建index.html,根據layui官網的API(后台布局代碼),引入相關代碼:

==注意:
html頁面中的標簽必須要加上對應的閉合標簽或標簽內加上"/",比如:<meta></meta> 或 <meta/>等;
在引入static/目錄下的css和js等文件時,路徑中不需要加"/static/",默認加載的是static/目錄下的文件;==
整合shiro權限控制
shiro簡介
Apache Shiro是一個功能強大、靈活的,開源的安全框架。它可以干凈利落地處理身份驗證、授權、企業會話管理和加密。
Apache Shiro的首要目標是易於使用和理解。安全通常很復雜,甚至讓人感到很痛苦,但是Shiro卻不是這樣子的。一個好的安全框架應該屏蔽復雜性,向外暴露簡單、直觀的API,來簡化開發人員實現應用程序安全所花費的時間和精力。
Shiro能做什么呢?
- 驗證用戶身份
- 用戶訪問權限控制,比如:1、判斷用戶是否分配了一定的安全角色。2、判斷用戶是否被授予完成某個操作的權限
- 在非 web 或 EJB 容器的環境下可以任意使用Session API
- 可以響應認證、訪問控制,或者 Session 生命周期中發生的事件
- 可將一個或以上用戶安全數據源數據組合成一個復合的用戶 "view"(視圖)
- 支持單點登錄(SSO)功能
- 支持提供“Remember Me”服務,獲取用戶關聯信息而無需登錄
- …
等等——都集成到一個有凝聚力的易於使用的API。根據官方的介紹,shiro提供了“身份認證”、“授權”、“加密”和“Session管理”這四個主要的核心功能
// TODO 百度
引入依賴
pom.xml中引入shiro依賴:
<!--spring boot 整合shiro依賴--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <!--shiro依賴--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>${shiro.version}</version> </dependency>
shiro.version版本為:1.3.1
shiro配置實體類
/** * @項目名稱:wyait-manage * @包名:com.wyait.manage.config * @類描述: * @創建人:wyait * @創建時間:2017-12-12 18:51 * @version:V1.0 */ @Configuration public class ShiroConfig { private static final Logger logger = LoggerFactory .getLogger(ShiroConfig.class); /** * ShiroFilterFactoryBean 處理攔截資源文件過濾器 * </br>1,配置shiro安全管理器接口securityManage; * </br>2,shiro 連接約束配置filterChainDefinitions; */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean( org.apache.shiro.mgt.SecurityManager securityManager) { //shiroFilterFactoryBean對象 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 配置shiro安全管理器 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 指定要求登錄時的鏈接 shiroFilterFactoryBean.setLoginUrl("/login"); // 登錄成功后要跳轉的鏈接 shiroFilterFactoryBean.setSuccessUrl("/index"); // 未授權時跳轉的界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // filterChainDefinitions攔截器 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); // 配置不會被攔截的鏈接 從上向下順序判斷 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/templates/**", "anon"); // 配置退出過濾器,具體的退出代碼Shiro已經替我們實現了 filterChainDefinitionMap.put("/logout", "logout"); //add操作,該用戶必須有【addOperation】權限 filterChainDefinitionMap.put("/add", "perms[addOperation]"); // <!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問【放行】--> filterChainDefinitionMap.put("/user/**", "authc"); shiroFilterFactoryBean .setFilterChainDefinitionMap(filterChainDefinitionMap); logger.debug("Shiro攔截器工廠類注入成功"); return shiroFilterFactoryBean; } /** * shiro安全管理器設置realm認證 * @return */ @Bean public org.apache.shiro.mgt.SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 設置realm. securityManager.setRealm(shiroRealm()); // //注入ehcache緩存管理器; securityManager.setCacheManager(ehCacheManager()); return securityManager; } /** * 身份認證realm; (賬號密碼校驗;權限等) * * @return */ @Bean public ShiroRealm shiroRealm() { ShiroRealm shiroRealm = new ShiroRealm(); return shiroRealm; } /** * ehcache緩存管理器;shiro整合ehcache: * 通過安全管理器:securityManager * @return EhCacheManager */ @Bean public EhCacheManager ehCacheManager() { logger.debug( "=====shiro整合ehcache緩存:ShiroConfiguration.getEhCacheManager()"); EhCacheManager cacheManager = new EhCacheManager(); cacheManager.setCacheManagerConfigFile("classpath:config/ehcache.xml"); return cacheManager; } }
Filter Chain定義說明:
1、一個URL可以配置多個Filter,使用逗號分隔;
2、當設置多個過濾器時,全部驗證通過,才視為通過;
3、部分過濾器可指定參數,如perms,roles
Shiro內置的FilterChain:
| Filter Name | Class |
|---|---|
| anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
| authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
| authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
| perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
| port | org.apache.shiro.web.filter.authz.PortFilter |
| rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
| roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
| ssl | org.apache.shiro.web.filter.authz.SslFilter |
| user | org.apache.shiro.web.filter.authc.UserFilter |
anon : 所有url都都可以匿名訪問
authc : 需要認證才能進行訪問
user : 配置記住我或認證通過可以訪問
ShiroRealm認證實體類
/** * @項目名稱:wyait-manage * @包名:com.wyait.manage.shiro * @類描述: * @創建人:wyait * @創建時間:2017-12-13 13:53 * @version:V1.0 */ public class ShiroRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principalCollection) { //TODO return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authenticationToken) throws AuthenticationException { //TODO return null; } }
shiro使用ehcache緩存
- 導入依賴;
<!--shiro添加ehcache緩存 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.6</version> </dependency> <!-- 包含支持UI模版(Velocity,FreeMarker,JasperReports), 郵件服務, 腳本服務(JRuby), 緩存Cache(EHCache), 任務計划Scheduling(uartz)。 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency>
- 引入ehcache.xml配置文件;
<ehcache>
<diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> <!-- 設定緩存的默認數據過期策略 --> <cache name="shiro" maxElementsInMemory="10000" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </cache> </ehcache>
- shiro配置類中整合ehcache做緩存管理;【參考:shiro配置實體類】
整合thymeleaf
- 導入pom依賴
<!--thymeleaf依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
- 配置中禁用緩存
#關閉thymeleaf緩存 spring.thymeleaf.cache=false
- springboot整合thymeleaf模版配置詳解:
| 參數 | 介紹 |
|---|---|
| spring.thymeleaf.cache = true | 啟用模板緩存(開發時建議關閉) |
| spring.thymeleaf.check-template = true | 檢查模板是否存在,然后再呈現 |
| spring.thymeleaf.check-template-location = true | 檢查模板位置是否存在 |
| spring.thymeleaf.content-type = text/html | Content-Type值 |
| spring.thymeleaf.enabled = true | 啟用MVC Thymeleaf視圖分辨率 |
| spring.thymeleaf.encoding = UTF-8 | 模板編碼 |
| spring.thymeleaf.excluded-view-names = | 應該從解決方案中排除的視圖名稱的逗號分隔列表 |
| spring.thymeleaf.mode = HTML5 | 應用於模板的模板模式。另請參見StandardTemplateModeHandlers |
| spring.thymeleaf.prefix = classpath:/templates/ | 在構建URL時預先查看名稱的前綴(默認/templates/) |
| spring.thymeleaf.suffix = .html | 構建URL時附加查看名稱的后綴 |
| spring.thymeleaf.template-resolver-order = | 鏈中模板解析器的順序 |
| spring.thymeleaf.view-names = | 可以解析的視圖名稱的逗號分隔列表 |
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties類里面有thymeleaf的默認配置。
默認頁面映射路徑為classpath:/templates/*.html
shiro功能之記住我
shiro記住我的功能是基於瀏覽器中的cookie實現的;
- 在shiroConfig里面增加cookie配置
- CookieRememberMeManager配置;
/** * 設置記住我cookie過期時間 * @return */ @Bean public SimpleCookie remeberMeCookie(){ logger.debug("記住我,設置cookie過期時間!"); //cookie名稱;對應前端的checkbox的name = rememberMe SimpleCookie scookie=new SimpleCookie("rememberMe"); //記住我cookie生效時間1小時 ,單位秒 [1小時] scookie.setMaxAge(3600); return scookie; } // 配置cookie記住我管理器 @Bean public CookieRememberMeManager rememberMeManager(){ logger.debug("配置cookie記住我管理器!"); CookieRememberMeManager cookieRememberMeManager=new CookieRememberMeManager(); cookieRememberMeManager.setCookie(remeberMeCookie()); return cookieRememberMeManager; }
- 將CookieRememberMeManager注入SecurityManager
//注入Cookie記住我管理器 securityManager.setRememberMeManager(rememberMeManager());
- 前端頁面新增rememberMe復選框
<input type="checkbox" name="rememberMe" lay-skin="primary" title="記住我"/>
- 登錄方法更改
//新增rememberMe參數 @RequestParam(value="rememberMe",required = false)boolean rememberMe ... ... // 1、 封裝用戶名、密碼、是否記住我到token令牌對象 [支持記住我] AuthenticationToken token = new UsernamePasswordToken( user.getMobile(), DigestUtils.md5Hex(user.getPassword()),rememberMe);
- 頁面cookie設置

shiro功能之密碼錯誤次數限制
針對用戶在登錄時用戶名和密碼輸入錯誤進行次數限制,並鎖定;
Shiro中用戶名密碼的驗證交給了CredentialsMatcher;
在CredentialsMatcher里面校驗用戶密碼,使用ehcache記錄登錄失敗次數就可以實現。
在驗證用戶名密碼之前先驗證登錄失敗次數,如果超過5次就拋出嘗試過多的異常,否則驗證用戶名密碼,驗證成功把嘗試次數清零,不成功則直接退出。這里依靠Ehcache自帶的timeToIdleSeconds來保證鎖定時間(帳號鎖定之后的最后一次嘗試間隔timeToIdleSeconds秒之后自動清除)。
- 自定義HashedCredentialsMatcher實現類
/** * @項目名稱:wyait-manage * @包名:com.wyait.manage.shiro * @類描述:shiro之密碼輸入次數限制6次,並鎖定2分鍾 * @創建人:wyait * @創建時間:2018年1月23日17:23:10 * @version:V1.0 */ public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher { //集群中可能會導致出現驗證多過5次的現象,因為AtomicInteger只能保證單節點並發 //解決方案,利用ehcache、redis(記錄錯誤次數)和mysql數據庫(鎖定)的方式處理:密碼輸錯次數限制; 或兩者結合使用 private Cache<String, AtomicInteger> passwordRetryCache; public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) { //讀取ehcache中配置的登錄限制鎖定時間 passwordRetryCache = cacheManager.getCache("passwordRetryCache"); } /** * 在回調方法doCredentialsMatch(AuthenticationToken token,AuthenticationInfo info)中進行身份認證的密碼匹配, * </br>這里我們引入了Ehcahe用於保存用戶登錄次數,如果登錄失敗retryCount變量則會一直累加,如果登錄成功,那么這個count就會從緩存中移除, * </br>從而實現了如果登錄次數超出指定的值就鎖定。 * @param token * @param info * @return */ @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { //獲取登錄用戶名 String username = (String) token.getPrincipal(); //從ehcache中獲取密碼輸錯次數 // retryCount AtomicInteger retryCount = passwordRetryCache.get(username); if (retryCount == null) { //第一次 retryCount = new AtomicInteger(0); passwordRetryCache.put(username, retryCount); } //retryCount.incrementAndGet()自增:count + 1 if (retryCount.incrementAndGet() > 5) { // if retry count > 5 throw 超過5次 鎖定 throw new ExcessiveAttemptsException("username:"+username+" tried to login more than 5 times in period"); } //否則走判斷密碼邏輯 boolean matches = super.doCredentialsMatch(token, info); if (matches) { // clear retry count 清楚ehcache中的count次數緩存 passwordRetryCache.remove(username); } return matches; } }
這里的邏輯也不復雜,在回調方法doCredentialsMatch(AuthenticationToken token,AuthenticationInfo info)
中進行身份認證的密碼匹配,這里我們引入了Ehcahe用於保存用戶登錄次數,如果登錄失敗retryCount變量則會一直累加,如果登錄成功,那么這個count就會從緩存中移除,從而實現了如果登錄次數超出指定的值就鎖定。
- ehcache中新增密碼重試次數緩存passwordRetryCache
<!-- 登錄記錄緩存 鎖定2分鍾 --> <cache name="passwordRetryCache" maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="0" overflowToDisk="false" statistics="false"> </cache>
- 在shiroConfig配置類中添加HashedCredentialsMatcher憑證匹配器
/** * 憑證匹配器 (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了 * 所以我們需要修改下doGetAuthenticationInfo中的代碼,更改密碼生成規則和校驗的邏輯一致即可; ) * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(ehCacheManager()); //new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:這里使用MD5算法; hashedCredentialsMatcher.setHashIterations(1);// 散列的次數,比如散列兩次,相當於 // md5(md5("")); return hashedCredentialsMatcher; }
- 設置ShiroRealm密碼匹配使用自定義的HashedCredentialsMatcher實現類
//使用自定義的CredentialsMatcher進行密碼校驗和輸錯次數限制 shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
- 更改ShiroRealm類doGetAuthenticationInfo登錄認證方法
更改密碼加密規則,和自定義的HashedCredentialsMatcher匹配器加密規則保持一致;
// 第一個參數 ,登陸后,需要在session保存數據 // 第二個參數,查詢到密碼(加密規則要和自定義的HashedCredentialsMatcher中的HashAlgorithmName散列算法一致) // 第三個參數 ,realm名字 new SimpleAuthenticationInfo(user, DigestUtils.md5Hex(user.getPassword()), getName());
- login方法的改動;
controller層獲取登錄失敗次數;登錄頁面新增用戶、密碼輸錯次數提醒;
//注入ehcache管理器 @Autowired private EhCacheManager ecm; ... ... //登錄方法中,獲取失敗次數,並設置友情提示信息 Cache<String, AtomicInteger> passwordRetryCache= ecm.getCache("passwordRetryCache"); if(null!=passwordRetryCache){ int retryNum=(passwordRetryCache.get(existUser.getMobile())==null?0:passwordRetryCache.get(existUser.getMobile())).intValue(); logger.debug("輸錯次數:"+retryNum); if(retryNum>0 && retryNum<6){ responseResult.setMessage("用戶名或密碼錯誤"+retryNum+"次,再輸錯"+(6-retryNum)+"次賬號將鎖定"); } }
- 后台新增用戶解鎖操作;清除ehcache中的緩存即可;
TODO
用戶列表,解鎖按鈕,點擊,彈出輸入框,讓用戶管理員輸入需要解鎖的用戶手機號,進行解鎖操作即可;
Cache<String, AtomicInteger> passwordRetryCache= ecm.getCache("passwordRetryCache"); //username是緩存key passwordRetryCache..remove(username);
thymeleaf整合shiro
html頁面使用thymeleaf模版;
- 導入pom依賴
<!--thymeleaf-shiro標簽--> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>1.2.1</version> </dependency>
thymeleaf整合shiro的依賴:thymeleaf-extras-shiro最新版本是2.0.0,配置使用報錯,所以使用1.2.1版本;
該jar包的github地址:https://github.com/theborakompanioni/thymeleaf-extras-shiro
- 配置shiroDirect
@Bean public ShiroDialect shiroDialect(){ return new ShiroDialect(); }
這段代碼放在ShiroConfig配置類里面即可。
- 頁面中使用
<html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> ... ... <!-- 獲取shiro中登錄的用戶名 --> <shiro:principal property="username"></shiro:principal>
具體用法,參考:https://github.com/theborakompanioni/thymeleaf-extras-shiro
整合pageHelper
- 導入pom依賴
<dependency>
<!-- pageHelper分頁插件 --> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.3</version> </dependency>
- 添加配置
# pagehelper參數配置 pagehelper.helperDialect=mysql pagehelper.reasonable=true pagehelper.supportMethodsArguments=true pagehelper.returnPageInfo=check pagehelper.params=count=countSql
- 代碼中使用
//PageHelper放在查詢方法前即可 PageHelper.startPage(page, limit); List<UserRoleDTO> urList = userMapper.getUsers(userSearch); ... ... //獲取分頁查詢后的pageInfo對象數據 PageInfo<UserRoleDTO> pageInfo = new PageInfo<>(urList); //pageInfo中獲取到的總記錄數total: pageInfo.getTotal();
PageInfo對象中的數據和用法,詳見源碼!
整合ztree
詳見ztree官網:http://www.treejs.cn/v3/api.php
整合httpClient
- 導入pom依賴
<!-- httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.3</version> </dependency> <!-- 提供FileBody、StringBody和MultipartEntity 使用httpClient上傳文件需要的類 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5.3</version> </dependency>
- 配置類
/** * @項目名稱:wyait-manage * @包名:com.wyait.manage.config * @類描述: * @創建人:wyait * @創建時間:2018-01-11 9:13 * @version:V1.0 */ @Configuration public class HttpClientConfig { private static final Logger logger = LoggerFactory .getLogger(ShiroConfig.class); /** * 連接池最大連接數 */ @Value("${httpclient.config.connMaxTotal}") private int connMaxTotal = 20; /** * */ @Value("${httpclient.config.maxPerRoute}") private int maxPerRoute = 20; /** * 連接存活時間,單位為s */ @Value("${httpclient.config.timeToLive}") private int timeToLive = 10; /** * 配置連接池 * @return */ @Bean(name="poolingClientConnectionManager") public PoolingHttpClientConnectionManager poolingClientConnectionManager(){ PoolingHttpClientConnectionManager poolHttpcConnManager = new PoolingHttpClientConnectionManager(60, TimeUnit.SECONDS); // 最大連接數 poolHttpcConnManager.setMaxTotal(this.connMaxTotal); // 路由基數 poolHttpcConnManager.setDefaultMaxPerRoute(this.maxPerRoute); return poolHttpcConnManager; } @Value("${httpclient.config.connectTimeout}") private int connectTimeout = 3000; @Value("${httpclient.config.connectRequestTimeout}") private int connectRequestTimeout = 2000; @Value("${httpclient.config.socketTimeout}") private int socketTimeout = 3000; /** * 設置請求配置 * @return */ @Bean public RequestConfig config(){ return RequestConfig.custom() .setConnectionRequestTimeout(this.connectRequestTimeout) .setConnectTimeout(this.connectTimeout) .setSocketTimeout(this.socketTimeout) .build(); } @Value("${httpclient.config.retryTime}")// 此處建議采用@ConfigurationProperties(prefix="httpclient.config")方式,方便復用 private int retryTime; /** * 重試策略 * @return */ @Bean public HttpRequestRetryHandler httpRequestRetryHandler() { // 請求重試 final int retryTime = this.retryTime; return new HttpRequestRetryHandler() { public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { // Do not retry if over max retry count,如果重試次數超過了retryTime,則不再重試請求 if (executionCount >= retryTime) { return false; } // 服務端斷掉客戶端的連接異常 if (exception instanceof NoHttpResponseException) { return true; } // time out 超時重試 if (exception instanceof InterruptedIOException) { return true; } // Unknown host if (exception instanceof UnknownHostException) { return false; } // Connection refused if (exception instanceof ConnectTimeoutException) { 