前言
Spring boot 是什么,網(wǎng)上的很多介紹褐荷,這里博客就不多介紹了。
如果不明白Spring boot是什么嘹悼。推薦幾篇文章叛甫,你可以了解下。
- Spring boot官網(wǎng)
-
初識Spring Boot框架
相似題材文章太多了杨伙,這里就不列舉多了其监。
還有一點就是篇幅有點長,請結(jié)合例子耐心看限匣。
- Shiro的簡介
- Spring boot中的使用
Shiro的簡介
Apache Shiro是Java的一個安全框架抖苦。目前,使用Apache Shiro的人越來越多,因為它相當(dāng)簡單锌历,對比Spring Security贮庞,可能沒有Spring Security做的功能強(qiáng)大,但是在實際工作時可能并不需要那么復(fù)雜的東西究西,所以使用小而簡單的Shiro就足夠了窗慎。對于它倆到底哪個好,這個不必糾結(jié)卤材,能更簡單的解決項目問題就好了遮斥。
本教程只介紹基本的Shiro使用,不會過多分析源碼等扇丛,重在使用伏伐。
Shiro可以非常容易的開發(fā)出足夠好的應(yīng)用,其不僅可以用在JavaSE環(huán)境晕拆,也可以用在JavaEE環(huán)境藐翎。Shiro可以幫助我們完成:認(rèn)證、授權(quán)实幕、加密吝镣、會話管理、與Web集成昆庇、緩存等末贾。這不就是我們想要的嘛,而且Shiro的API也是非常簡單整吆;其基本功能點如下圖所示:
Authentication:身份認(rèn)證/登錄拱撵,驗證用戶是不是擁有相應(yīng)的身份;
Authorization:授權(quán)表蝙,即權(quán)限驗證拴测,驗證某個已認(rèn)證的用戶是否擁有某個權(quán)限;即判斷用戶是否能做事情府蛇,常見的如:驗證某個用戶是否擁有某個角色集索。或者細(xì)粒度的驗證某個用戶對某個資源是否具有某個權(quán)限汇跨;
Session Manager:會話管理务荆,即用戶登錄后就是一次會話,在沒有退出之前穷遂,它的所有信息都在會話中函匕;會話可以是普通JavaSE環(huán)境的,也可以是如Web環(huán)境的蚪黑;
Cryptography:加密盅惜,保護(hù)數(shù)據(jù)的安全性中剩,如密碼加密存儲到數(shù)據(jù)庫,而不是明文存儲酷窥;
Web Support:Web支持咽安,可以非常容易的集成到Web環(huán)境;
Caching:緩存蓬推,比如用戶登錄后妆棒,其用戶信息、擁有的角色/權(quán)限不必每次去查沸伏,這樣可以提高效率糕珊;
Concurrency:shiro支持多線程應(yīng)用的并發(fā)驗證,即如在一個線程中開啟另一個線程毅糟,能把權(quán)限自動傳播過去红选;
Testing:提供測試支持;
Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進(jìn)行訪問姆另;
Remember Me:記住我喇肋,這個是非常常見的功能,即一次登錄后迹辐,下次再來的話不用登錄了蝶防。
記住一點,Shiro不會去維護(hù)用戶明吩、維護(hù)權(quán)限间学;這些需要我們自己去設(shè)計/提供;然后通過相應(yīng)的接口注入給Shiro即可印荔。
接下來我們分別從外部和內(nèi)部來看看Shiro的架構(gòu)低葫,對于一個好的框架,從外部來看應(yīng)該具有非常簡單易于使用的API仍律,且API契約明確嘿悬;從內(nèi)部來看的話,其應(yīng)該有一個可擴(kuò)展的架構(gòu)染苛,即非常容易插入用戶自定義實現(xiàn)鹊漠,因為任何框架都不能滿足所有需求。
可以看到:應(yīng)用代碼直接交互的對象是Subject茶行,也就是說Shiro的對外API核心就是Subject;其每個API的含義:
Subject:主體登钥,代表了當(dāng)前“用戶”畔师,這個用戶不一定是一個具體的人,與當(dāng)前應(yīng)用交互的任何東西都是Subject牧牢,如網(wǎng)絡(luò)爬蟲看锉,機(jī)器人等姿锭;即一個抽象概念;所有Subject都綁定到SecurityManager伯铣,與Subject的所有交互都會委托給SecurityManager呻此;可以把Subject認(rèn)為是一個門面;SecurityManager才是實際的執(zhí)行者腔寡;
SecurityManager:安全管理器焚鲜;即所有與安全有關(guān)的操作都會與SecurityManager交互;且它管理著所有Subject放前;可以看出它是Shiro的核心忿磅,它負(fù)責(zé)與后邊介紹的其他組件進(jìn)行交互,如果學(xué)習(xí)過SpringMVC凭语,你可以把它看成DispatcherServlet前端控制器葱她;
Realm:域,Shiro從從Realm獲取安全數(shù)據(jù)(如用戶似扔、角色吨些、權(quán)限),就是說SecurityManager要驗證用戶身份炒辉,那么它需要從Realm獲取相應(yīng)的用戶進(jìn)行比較以確定用戶身份是否合法豪墅;也需要從Realm得到用戶相應(yīng)的角色/權(quán)限進(jìn)行驗證用戶是否能進(jìn)行操作;可以把Realm看成DataSource辆脸,即安全數(shù)據(jù)源但校。
也就是說對于我們而言,最簡單的一個Shiro應(yīng)用:
1啡氢、應(yīng)用代碼通過Subject來進(jìn)行認(rèn)證和授權(quán)状囱,而Subject又委托給SecurityManager;
2倘是、我們需要給Shiro的SecurityManager注入Realm亭枷,從而讓SecurityManager能得到合法的用戶及其權(quán)限進(jìn)行判斷。
從以上也可以看出搀崭,Shiro不提供維護(hù)用戶/權(quán)限叨粘,而是通過Realm讓開發(fā)人員自己注入。
接下來我們來從Shiro內(nèi)部來看下Shiro的架構(gòu)瘤睹,如下圖所示:
Subject:主體升敲,可以看到主體可以是任何可以與應(yīng)用交互的“用戶”;
SecurityManager:相當(dāng)于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher轰传;是Shiro的心臟驴党;所有具體的交互都通過SecurityManager進(jìn)行控制;它管理著所有Subject获茬、且負(fù)責(zé)進(jìn)行認(rèn)證和授權(quán)港庄、及會話倔既、緩存的管理。
Authenticator:認(rèn)證器鹏氧,負(fù)責(zé)主體認(rèn)證的渤涌,這是一個擴(kuò)展點,如果用戶覺得Shiro默認(rèn)的不好把还,可以自定義實現(xiàn)实蓬;其需要認(rèn)證策略(Authentication Strategy),即什么情況下算用戶認(rèn)證通過了笨篷;
Authrizer:授權(quán)器瞳秽,或者訪問控制器,用來決定主體是否有權(quán)限進(jìn)行相應(yīng)的操作率翅;即控制著用戶能訪問應(yīng)用中的哪些功能练俐;
Realm:可以有1個或多個Realm,可以認(rèn)為是安全實體數(shù)據(jù)源冕臭,即用于獲取安全實體的腺晾;可以是JDBC實現(xiàn),也可以是LDAP實現(xiàn)辜贵,或者內(nèi)存實現(xiàn)等等悯蝉;由用戶提供;注意:Shiro不知道你的用戶/權(quán)限存儲在哪及以何種格式存儲托慨;所以我們一般在應(yīng)用中都需要實現(xiàn)自己的Realm鼻由;
SessionManager:如果寫過Servlet就應(yīng)該知道Session的概念,Session呢需要有人去管理它的生命周期厚棵,這個組件就是SessionManager蕉世;而Shiro并不僅僅可以用在Web環(huán)境,也可以用在如普通的JavaSE環(huán)境婆硬、EJB等環(huán)境狠轻;所有呢,Shiro就抽象了一個自己的Session來管理主體與應(yīng)用之間交互的數(shù)據(jù)彬犯;這樣的話向楼,比如我們在Web環(huán)境用,剛開始是一臺Web服務(wù)器谐区;接著又上了臺EJB服務(wù)器湖蜕;這時想把兩臺服務(wù)器的會話數(shù)據(jù)放到一個地方,這個時候就可以實現(xiàn)自己的分布式會話(如把數(shù)據(jù)放到Memcached服務(wù)器)宋列;
SessionDAO:DAO大家都用過重荠,數(shù)據(jù)訪問對象,用于會話的CRUD虚茶,比如我們想把Session保存到數(shù)據(jù)庫戈鲁,那么可以實現(xiàn)自己的SessionDAO,通過如JDBC寫到數(shù)據(jù)庫嘹叫;比如想把Session放到Memcached中婆殿,可以實現(xiàn)自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache進(jìn)行緩存罩扇,以提高性能婆芦;
CacheManager:緩存控制器,來管理如用戶喂饥、角色消约、權(quán)限等的緩存的;因為這些數(shù)據(jù)基本上很少去改變员帮,放到緩存中后可以提高訪問的性能
Cryptography:密碼模塊或粮,Shiro提高了一些常見的加密組件用于如密碼加密/解密的。
Spring boot中的使用
demo實現(xiàn)記住我捞高,驗證碼登錄氯材,權(quán)限自定義(or),緩存功能等硝岗。
你們看這篇博客可以先把項目下載下來氢哮,里面的注釋我都寫的比較完整。
核心依賴
<!-- shiro 權(quán)限控制 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
緩存依賴
<!-- shiro ehcache (shiro緩存)-->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
這里使用了驗證碼框架是google的kaptcha
<!--google的驗證碼框架-->
<dependency>
<groupId>com.google.code.kaptcha</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3</version>
</dependency>
測試用的模板引擎是thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
1.新建ShiroConfiguration類
Shiro有幾個核心的類型檀,第一個在于ShiroFilterFactoryBean冗尤,第二個就是SecurityManager,代碼如下:
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Created with IntelliJ IDEA.
* packageName : com.xuezhijian.shiro
* User : zj
* Date : 17/2/13
* Time : 下午11:14
* Description : Apache Shiro 核心通過 Filter 來實現(xiàn)胀溺,就好像SpringMvc 通過DispachServlet 來主控制一樣裂七。
* 既然是使用 Filter 一般也就能猜到,是通過URL規(guī)則來進(jìn)行過濾和權(quán)限校驗月幌,所以我們需要定義一系列關(guān)于URL的規(guī)則和訪問權(quán)限碍讯。
*/
@Configuration
@Order(1)
public class ShiroConfiguration {
/**
* ShiroFilterFactoryBean 處理攔截資源文件問題。
* 注意:單獨一個ShiroFilterFactoryBean配置是或報錯的扯躺,以為在
* 初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager Filter Chain定義說明
* 1捉兴、一個URL可以配置多個Filter,使用逗號分隔 2录语、當(dāng)設(shè)置多個過濾器時倍啥,全部驗證通過,才視為通過
* 3澎埠、部分過濾器可指定參數(shù)虽缕,如perms,roles
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必須設(shè)置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
//驗證碼過濾器
Map<String, Filter> filtersMap = shiroFilterFactoryBean.getFilters();
KaptchaFilter kaptchaFilter = new KaptchaFilter();
filtersMap.put("kaptchaFilter", kaptchaFilter);
//實現(xiàn)自己規(guī)則roles,這是為了實現(xiàn)or的效果
//RoleFilter roleFilter = new RoleFilter();
//filtersMap.put("roles", roleFilter);
shiroFilterFactoryBean.setFilters(filtersMap);
// 攔截器.
//rest:比如/admins/user/**=rest[user],根據(jù)請求的方法蒲稳,相當(dāng)于/admins/user/**=perms[user:method] ,其中method為post氮趋,get伍派,delete等。
//port:比如/admins/user/**=port[8081],當(dāng)請求的url的端口不是8081是跳轉(zhuǎn)到schemal://serverName:8081?queryString,其中schmal是協(xié)議http或https等剩胁,serverName是你訪問的host,8081是url配置里port的端口诉植,queryString是你訪問的url里的?后面的參數(shù)昵观。
//perms:比如/admins/user/**=perms[user:add:*],perms參數(shù)可以寫多個晾腔,多個時必須加上引號,并且參數(shù)之間用逗號分割啊犬,比如/admins/user/**=perms["user:add:*,user:modify:*"]灼擂,當(dāng)有多個參數(shù)時必須每個參數(shù)都通過才通過,想當(dāng)于isPermitedAll()方法觉至。
//roles:比如/admins/user/**=roles[admin],參數(shù)可以寫多個剔应,多個時必須加上引號,并且參數(shù)之間用逗號分割康谆,當(dāng)有多個參數(shù)時领斥,比如/admins/user/**=roles["admin,guest"],每個參數(shù)通過才算通過,相當(dāng)于hasAllRoles()方法沃暗。//要實現(xiàn)or的效果看http://zgzty.blog.163.com/blog/static/83831226201302983358670/
//anon:比如/admins/**=anon 沒有參數(shù)月洛,表示可以匿名使用。
//authc:比如/admins/user/**=authc表示需要認(rèn)證才能使用孽锥,沒有參數(shù)
//authcBasic:比如/admins/user/**=authcBasic沒有參數(shù)表示httpBasic認(rèn)證
//ssl:比如/admins/user/**=ssl沒有參數(shù)嚼黔,表示安全的url請求,協(xié)議為https
//user:比如/admins/user/**=user沒有參數(shù)表示必須存在用戶惜辑,當(dāng)?shù)侨氩僮鲿r不做檢查
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置退出過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實現(xiàn)了
filterChainDefinitionMap.put("/logout", "logout");
//配置記住我或認(rèn)證通過可以訪問的地址
filterChainDefinitionMap.put("/index", "user");
filterChainDefinitionMap.put("/", "user");
filterChainDefinitionMap.put("/login", "kaptchaFilter");
// <!-- 過濾鏈定義唬涧,從上向下順序執(zhí)行,一般將 /**放在最為下邊 -->:這是一個坑呢盛撑,一不小心代碼就不好使了;
//這段是配合 actuator框架使用的碎节,配置相應(yīng)的角色才能訪問
// filterChainDefinitionMap.put("/health", "roles[aix]");//服務(wù)器健康狀況頁面
// filterChainDefinitionMap.put("/info", "roles[aix]");//服務(wù)器信息頁面
// filterChainDefinitionMap.put("/env", "roles[aix]");//應(yīng)用程序的環(huán)境變量
// filterChainDefinitionMap.put("/metrics", "roles[aix]");
// filterChainDefinitionMap.put("/configprops", "roles[aix]");
//開放的靜態(tài)資源
filterChainDefinitionMap.put("/favicon.ico", "anon");//網(wǎng)站圖標(biāo)
filterChainDefinitionMap.put("/AdminLTE-2.3.7/**", "anon");//配置static文件下資源能被訪問的,這是個例子
filterChainDefinitionMap.put("/kaptcha.jpg", "anon");//圖片驗證碼(kaptcha框架)
filterChainDefinitionMap.put("/**", "authc");
// 如果不設(shè)置默認(rèn)會自動尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登錄成功后要跳轉(zhuǎn)的鏈接
shiroFilterFactoryBean.setSuccessUrl("/index");
// 未授權(quán)界面
shiroFilterFactoryBean.setUnauthorizedUrl("/errorView/403_error.html");//不生效(詳情原因看MyExceptionResolver)
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 設(shè)置realm.
securityManager.setRealm(myShiroRealm());
//注入緩存管理器;
//注意:開發(fā)時請先關(guān)閉抵卫,如不關(guān)閉熱啟動會報錯
//securityManager.setCacheManager(ehCacheManager());//這個如果執(zhí)行多次狮荔,也是同樣的一個對象;
//注入記住我管理器;
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
/**
* 身份認(rèn)證realm; (這個需要自己寫,賬號密碼校驗介粘;權(quán)限等)
*/
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
/**
* 憑證匹配器 (由于我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進(jìn)行處理了
* 所以我們需要修改下doGetAuthenticationInfo中的代碼; @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:這里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);// 散列的次數(shù)殖氏,比如散列兩次,相當(dāng)于md5(md5(""));
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);//表示是否存儲散列后的密碼為16進(jìn)制姻采,需要和生成密碼時的一樣雅采,默認(rèn)是base64;
return hashedCredentialsMatcher;
}
/**
* 開啟shiro aop注解支持. 使用代理方式;所以需要開啟代碼支持;
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* shiro緩存管理器;
* 需要注入對應(yīng)的其它的實體類中:
* 1、安全管理器:securityManager
* 可見securityManager是整個shiro的核心婚瓜;
*
* @return
*/
@Bean
public EhCacheManager ehCacheManager(){
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
return cacheManager;
}
/**
* cookie對象;
* @return
* */
@Bean
public SimpleCookie rememberMeCookie(){
//System.out.println("ShiroConfiguration.rememberMeCookie()");
//這個參數(shù)是cookie的名稱宝鼓,對應(yīng)前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//<!-- 記住我cookie生效時間30天 ,單位秒;-->
simpleCookie.setMaxAge(259200);
return simpleCookie;
}
/**
* cookie管理對象;
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
//System.out.println("ShiroConfiguration.rememberMeManager()");
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
return cookieRememberMeManager;
}
}
以上代碼的解釋:
shirFilter方法是注入ShiroFilterFactoryBean相關(guān)屬性:
設(shè)置SecurityManager安全管理器:(下面會有詳細(xì)點的介紹)
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必須設(shè)置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
設(shè)置攔截規(guī)則:這個map可配置key-value去完成相應(yīng)的攔截操作,例如配置登錄闰渔,登出席函,哪些靜態(tài)資源需要權(quán)限管理或者排除在權(quán)限管理之外。還可以自定義過濾器.demo中有驗證碼過濾器例子冈涧。
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
通過ShiroFilterFactoryBean注入:
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
代碼中都有更詳細(xì)的解釋說明。
- SecurityManager的注入,結(jié)合自定義的MyShiroRealm(身份認(rèn)證)正蛙,查詢數(shù)據(jù)庫的用戶信息和權(quán)限,對用戶密碼進(jìn)行加鹽加密督弓。
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 設(shè)置realm.
securityManager.setRealm(myShiroRealm());
//注入緩存管理器;
//注意:開發(fā)時請先關(guān)閉,如不關(guān)閉熱啟動會報錯
//securityManager.setCacheManager(ehCacheManager());//這個如果執(zhí)行多次乒验,也是同樣的一個對象;
//注入記住我管理器;
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
MyShiroRealm的代碼:
package com.xuezhijian.shiro;
import com.xuezhijian.dao.entity.ManagerInfo;
import com.xuezhijian.dao.entity.SysPermission;
import com.xuezhijian.dao.entity.SysRole;
import com.xuezhijian.service.ManagerInfoService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Created with IntelliJ IDEA.
* packageName : com.xuezhijian.shiro
* User : zj
* Date : 17/2/13
* Time : 下午11:28
* Description : 身份校驗核心類
*/
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
ManagerInfoService managerInfoService;
/**
* 認(rèn)證信息.(身份驗證)
* Authentication 是用來驗證用戶身份
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
//System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
//獲取用戶的輸入的賬號.
String username = (String)token.getPrincipal();
//System.out.println("用戶的賬號:"+username);
//通過username從數(shù)據(jù)庫中查找 ManagerInfo對象
//實際項目中愚隧,這里可以根據(jù)實際情況做緩存,如果不做锻全,Shiro自己也是有時間間隔機(jī)制狂塘,2分鐘內(nèi)不會重復(fù)執(zhí)行該方法
ManagerInfo managerInfo = managerInfoService.findByUsername(username);
// System.out.println("----->>managerInfo="+managerInfo.toString());
if(managerInfo == null){
return null;
}
//交給AuthenticatingRealm使用CredentialsMatcher進(jìn)行密碼匹配,如果覺得人家的不好可以自定義實現(xiàn)
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
managerInfo, //用戶名
managerInfo.getPassword(), //密碼
ByteSource.Util.bytes(managerInfo.getCredentialsSalt()),//salt=username+salt
getName() //realm name
);
//明文: 若存在鳄厌,將此用戶存放到登錄認(rèn)證info中荞胡,無需自己做密碼對比,Shiro會為我們進(jìn)行密碼對比校驗
// SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
// managerInfo, //用戶名
// managerInfo.getPassword(), //密碼
// getName() //realm name
// );
return authenticationInfo;
}
/**
* 此方法調(diào)用 hasRole,hasPermission的時候才會進(jìn)行回調(diào).
*
* 權(quán)限信息.(授權(quán)):
* 1了嚎、如果用戶正常退出泪漂,緩存自動清空;
* 2歪泳、如果用戶非正常退出萝勤,緩存自動清空;
* 3呐伞、如果我們修改了用戶的權(quán)限敌卓,而用戶不退出系統(tǒng),修改的權(quán)限無法立即生效伶氢。
* (需要手動編程進(jìn)行實現(xiàn)趟径;放在service進(jìn)行調(diào)用)
* 在權(quán)限修改后調(diào)用realm中的方法,realm已經(jīng)由spring管理鞍历,所以從spring中獲取realm實例舵抹,
* 調(diào)用clearCached方法;
* :Authorization 是授權(quán)訪問控制劣砍,用于對用戶進(jìn)行的操作授權(quán)惧蛹,證明該用戶是否允許進(jìn)行當(dāng)前操作,如訪問某個鏈接,某個資源文件等香嗓。
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
/*
* 當(dāng)沒有使用緩存的時候迅腔,不斷刷新頁面的話,這個代碼會不斷執(zhí)行靠娱,
* 當(dāng)其實沒有必要每次都重新設(shè)置權(quán)限信息沧烈,所以我們需要放到緩存中進(jìn)行管理;
* 當(dāng)放到緩存中時像云,這樣的話锌雀,doGetAuthorizationInfo就只會執(zhí)行一次了,
* 緩存過期之后會再次執(zhí)行迅诬。
*/
//System.out.println("權(quán)限配置-->MyShiroRealm.doGetAuthorizationInfo()");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
ManagerInfo managerInfo = (ManagerInfo)principals.getPrimaryPrincipal();
//實際項目中腋逆,這里可以根據(jù)實際情況做緩存,如果不做侈贷,Shiro自己也是有時間間隔機(jī)制惩歉,2分鐘內(nèi)不會重復(fù)執(zhí)行該方法
// UserInfo userInfo = userInfoService.findByUsername(username)
//設(shè)置相應(yīng)角色的權(quán)限信息
for(SysRole role:managerInfo.getRoles()){
//設(shè)置角色
authorizationInfo.addRole(role.getRole());
for(SysPermission p:role.getPermissions()){
//設(shè)置權(quán)限
authorizationInfo.addStringPermission(p.getPermission());
}
}
return authorizationInfo;
}
}
3.如果想實現(xiàn)驗證碼功能的話
可以繼承FormAuthenticationFilter授權(quán)過濾器拓展自已的驗證碼功能
package com.xuezhijian.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
* Created with IntelliJ IDEA.
* packageName : com.xuezhijian.shiro
* User : zj
* Date : 17/2/13
* Time : 下午11:30
* Description : 驗證碼過濾器此過濾器已經(jīng)在shiro中配置,這里不需要再次配置攔截路徑
*/
public class KaptchaFilter extends FormAuthenticationFilter {
public static final String DEFAULT_CAPTCHA_PARAM = "captcha";
private String captchaParam = DEFAULT_CAPTCHA_PARAM;
//登錄驗證
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response)
throws Exception {
CaptchaUsernamePasswordToken token = createToken(request, response);
String username = token.getUsername();
try {
//System.out.println("KaptchaFilter.executeLogin");
/*圖形驗證碼驗證*/
doCaptchaValidate((HttpServletRequest) request, token);
Subject subject = getSubject(request, response);
subject.login(token);//正常驗證
//到這里就算驗證成功了,把用戶信息放到session中
((HttpServletRequest) request).getSession().setAttribute("name",username);
return onLoginSuccess(token, subject, request, response);
}catch (AuthenticationException e) {
return onLoginFailure(token, e, request, response);
}
}
// 驗證碼校驗
protected void doCaptchaValidate(HttpServletRequest request, CaptchaUsernamePasswordToken token) {
//System.out.println("KaptchaFilter.doCaptchaValidate");
//session中的圖形碼字符串
String captcha = (String) request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
//System.out.println("session中的圖形碼字符串:"+captcha);
//比對
if (captcha == null || !captcha.equalsIgnoreCase(token.getCaptcha())) {
throw new IncorrectCaptchaException();
}
}
@Override
protected CaptchaUsernamePasswordToken createToken(ServletRequest request, ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
String captcha = getCaptcha(request);
boolean rememberMe = isRememberMe(request);
String host = getHost(request);
return new CaptchaUsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha);
}
public String getCaptchaParam() {
return captchaParam;
}
public void setCaptchaParam(String captchaParam) {
this.captchaParam = captchaParam;
}
protected String getCaptcha(ServletRequest request) {
return WebUtils.getCleanParam(request, getCaptchaParam());
}
//保存異常對象到request
@Override
protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
request.setAttribute(getFailureKeyAttribute(), ae);
}
}
4.shiro緩存管理器,因為我們不可能每次都要去查庫查詢用戶的權(quán)限信息俏蛮,應(yīng)該是查詢一次后放在緩存中撑蚌,下次去緩存中去查找,設(shè)定緩存時效,ehcache-shiro.xml只是簡單的配置下緩存的策略搏屑,具體可在項目中查看争涌,還有這里有實現(xiàn)rememberMe功能,核心代碼如下睬棚,
/**
* shiro緩存管理器;
* 需要注入對應(yīng)的其它的實體類中:
* 1第煮、安全管理器:securityManager
* 可見securityManager是整個shiro的核心;
*
* @return
*/
@Bean
public EhCacheManager ehCacheManager(){
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
return cacheManager;
}
/**
* cookie對象;
* @return
* */
@Bean
public SimpleCookie rememberMeCookie(){
//System.out.println("ShiroConfiguration.rememberMeCookie()");
//這個參數(shù)是cookie的名稱抑党,對應(yīng)前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//<!-- 記住我cookie生效時間30天 ,單位秒;-->
simpleCookie.setMaxAge(259200);
return simpleCookie;
}
/**
* cookie管理對象;
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
//System.out.println("ShiroConfiguration.rememberMeManager()");
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
return cookieRememberMeManager;
}
demo項目結(jié)構(gòu):
你可以把項目下載下來包警,修改下數(shù)據(jù)庫(項目中帶sql腳本,簡單的權(quán)限底靠,因為是demo就不做復(fù)雜的權(quán)限的表設(shè)計了害晦。)