一 、Shiro入門
1.簡介
- Apache Shiro 是 Java 的一個安全(權(quán)限)框架来屠。
- Shiro 可以非常容易的開發(fā)出足夠好的應(yīng)用,其不僅可以用在 JavaSE 環(huán)境,也可以用在 JavaEE 環(huán)境虑椎。
- Shiro 可以完成:認證、授權(quán)俱笛、加密捆姜、會話管理、與Web 集成迎膜、緩存等泥技。
- 官網(wǎng):http://shiro.apache.org/
- 本文參考http://shiro.apache.org/authentication.html
2.基本功能
- Authentication:身份認證/登錄,驗證用戶是不是擁有相應(yīng)的身份;
- Authorization:授權(quán),即權(quán)限驗證,驗證某個已認證的用戶是否擁有某個權(quán)限;即判斷用戶是否能進行什么操作,如:驗證某個用戶是否擁有某個角色⌒呛纾或者細粒度的驗證某個用對某個資源是否具有某個權(quán)限;
- Session Manager:會話管理,即用戶登錄后就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通 JavaSE 環(huán)境,也可以是 Web 環(huán)境的;
- Cryptography:加密,保護數(shù)據(jù)的安全性,如密碼加密存儲到數(shù)據(jù)庫,而不是明文存儲;
- Web Support:Web 支持,可以非常容易的集成到Web 環(huán)境;
- Caching:緩存,比如用戶登錄后,其用戶信息零抬、擁有的角色/權(quán)限不必每次去查,這樣可以提高效率;
- Concurrency:Shiro 支持多線程應(yīng)用的并發(fā)驗證,即如在一個線程中開啟另一個線程,能把權(quán)限自動傳播過去;
- Testing:提供測試支持;
- Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問;
- Remember Me:記住我,這個是非常常見的功能,即一次登錄后,下次再來的話不用登錄了]
3.Shiro 架構(gòu)
從外部來看Shiro ,即從應(yīng)用程序角度的來觀察如何使用 Shiro 完成工作:
- Subject:應(yīng)用代碼直接交互的對象是 Subject,也就是說 Shiro 的對外API 核心就是 Subject。Subject 代表了當前“用戶”, 這個用戶不一定是一個具體的人,與當前應(yīng)用交互的任何東西都是 Subject,如網(wǎng)絡(luò)爬蟲,機器人等;與 Subject 的所有交互都會委托給 SecurityManager; Subject 其實是一個門面,SecurityManager 才是實際的執(zhí)行者;Subject用于獲取主體信息宽涌,principals和credentials
- SecurityManager:安全管理器;即所有與安全有關(guān)的操作都會與 SecurityManager 交互;且其管理著所有 Subject;可以看出它是 Shiro的核心,它負責(zé)與 Shiro 的其他組件進行交互,它相當于 SpringMVC 中DispatcherServlet 的角色
- Realm:Shiro 從 Realm 獲取安全數(shù)據(jù)(如用戶平夜、角色、權(quán)限),就是說SecurityManager 要驗證用戶身份,那么它需要從 Realm 獲取相應(yīng)的用戶進行比較以確定用戶身份是否合法;也需要從 Realm 得到用戶相應(yīng)的角色/權(quán)限進行驗證用戶是否能進行操作;可以把 Realm 看成 DataSource
從內(nèi)部看Shiro架構(gòu)
- Subject:任何可以與應(yīng)用交互的“用戶”;
- SecurityManager :相當于SpringMVC 中的 DispatcherServlet;是 Shiro 的心臟;所有具體的交互都通過 SecurityManager 進行控制;它管理著所有 Subject卸亮、且負責(zé)進行認證忽妒、授權(quán)、會話及緩存的管理兼贸。
- Authenticator:負責(zé) Subject 認證,是一個擴展點,可以自定義實現(xiàn);可以使用認證策略(Authentication Strategy),即什么情況下算用戶認證通過了;
- Authorizer:授權(quán)器段直、即訪問控制器,用來決定主體是否有權(quán)限進行相應(yīng)的操作;即控制著用戶能訪問應(yīng)用中的哪些功能;
- Realm:可以有 1 個或多個 Realm,可以認為是安全實體數(shù)據(jù)源,即用于獲取安全實體的;可以是JDBC 實現(xiàn),也可以是內(nèi)存實現(xiàn)等等;由用戶提供;所以一般在應(yīng)用中都需要實現(xiàn)自己的 Realm;
- SessionManager:管理 Session 生命周期的組件;而 Shiro 并不僅僅可以用在 Web環(huán)境,也可以用在如普通的 JavaSE 環(huán)境
- CacheManager:緩存控制器,來管理如用戶、角色溶诞、權(quán)限等的緩存的;因為這些數(shù)據(jù)基本上很少改變,放到緩存中后可以提高訪問的性能
- Cryptography:密碼模塊,Shiro 提高了一些常見的加密組件用于如密碼加密/解密鸯檬。
開篇我們對Shiro已經(jīng)有了初步的認識,下面我們將從代碼的角度進一步學(xué)習(xí)Shiro的工作流程螺垢。
二喧务、認證
1.Authentication
用戶認證赖歌,即通過提交用戶的身份和憑證給Shiro,驗證用戶是否合法功茴。
- Principles:用戶的身份信息庐冯,是Subject的標識屬性,能夠唯一標識Subject坎穿,如電話號碼展父,電子郵箱,身份證號碼玲昧。
- Credentials:憑證栖茉,是只被Subject知道的秘密值,可以是密碼酌呆,也可以是數(shù)字證書衡载。
- Principles/Credentials最常見的組合:用戶名、密碼隙袁。在Shiro中通常使用UsernamePasswordToken來指定身份和憑證信息痰娱。
2.用戶認證流程
- 應(yīng)用程序調(diào)用Subject.login方法,傳遞創(chuàng)建好的包含終端用戶的Principles/Credentials的AuthenticationToken實例菩收。
- Subject實例梨睁,通常是DelegatingSubject(或子類)委托應(yīng)用程序的SecurityManager調(diào)用securityManager.login(token)開始真正的驗證工作。
- SecurityManager接收token并通過調(diào)用authenticator.authenticate(token)將其委托給內(nèi)部的Authenticator實例娜饵。它一般是ModularRealmAuthenticator實例坡贺,它支持在身份驗證期間協(xié)調(diào)一個或多個域?qū)嵗?/li>
- 如果為應(yīng)用程序配置了多個域,ModularRealmAuthenticator實例將使用其配置的AuthenticationStrategy發(fā)起多域身份驗證嘗試箱舞。在為身份驗證調(diào)用領(lǐng)域之前遍坟、期間和之后,將調(diào)用AuthenticationStrategy晴股,以允許它對每個領(lǐng)域的結(jié)果作出反應(yīng)愿伴。如果只配置了單個域,則直接調(diào)用它——在單個域應(yīng)用程序中不需要驗證策略电湘。
- 查看已配置的域是否支持提交的AuthenticationToken隔节,如果支持,將使用提交的令牌調(diào)用支持域的getAuthenticationInfo方法寂呛。getAuthenticationInfo方法有效地表示該特定領(lǐng)域的單個身份驗證嘗試怎诫。我們將很快介紹領(lǐng)域身份驗證行為。
3.代碼實現(xiàn)
- 新建項目贷痪,添加shiro相關(guān)的依賴
<dependencies>
<!--單元測試-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
</dependencies>
- 添加日志配置文件log4j.properties
log4j.rootLogger=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
- 編寫shiro的數(shù)據(jù)文件幻妓,準備一些用戶身份/憑據(jù)(shiro.ini)
[users]
TiaNa=1111
Tim=1111
- 主體代碼
public class Authentication{
public static void main(String[] args) {
//1 創(chuàng)建SecurityManager工廠,讀取相應(yīng)的配置文件
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2 通過SecurityManager工廠獲取實例
SecurityManager securityManager = factory.getInstance();
//3 將securityManager對象設(shè)置到運行環(huán)境中
SecurityUtils.setSecurityManager(securityManager);
//4 通過SecurityUtils獲取主體Subject
Subject subject = SecurityUtils.getSubject();
//5 創(chuàng)建用戶名/密碼身份驗證 Token
UsernamePasswordToken token = new UsernamePasswordToken("TiaNa", "1111");
try {
//6 進行用戶身份驗證
subject.login(token);
//7 通過subject判斷用戶是否通過驗證
if(subject.isAuthenticated()){
System.out.println("登錄成功");
}
}catch (AuthenticationException e){
System.out.println("登錄失敗");
}
}
}
- 上述代碼我是直接拋出了AuthenticationException 劫拢,它其實是Shiro中所有異常的父類涌哲,在Shiro中的異常如圖所示:
雖然Shiro為每一種異常都提供了準確的異常類胖缤,但為了安全,提供給用戶的異常信息應(yīng)該是模糊的阀圾,通常處理方式為:
try {
//6 進行用戶身份驗證
subject.login(token);
//7 通過subject判斷用戶是否通過驗證
if(subject.isAuthenticated()){
System.out.println("登錄成功");
}
}catch (UnknownAccountException e){
System.out.println("用戶或密碼不正確");
}catch (IncorrectCredentialsException e){
System.out.println("用戶或密碼不正確");
}
三 、JdbcRealm
Shiro 默認使用自帶的 IniRealm狗唉,IniRealm 從 ini 配置文件中讀取用戶的信息初烘,首先我們可以學(xué)習(xí)Shiro所提供的JdbcRealm的使用。
1.ini配置文件詳解
我們提到分俯,IniRealm 從ini 配置文件中讀取用戶的信息肾筐,所以先從ini配置文件開始講起。
- [main]section 是你配置應(yīng)用程序的SecurityManager實例及任何它的依賴組件(如Realm)的地方缸剪。
[main]
myRealm=cn.cxt.realm.myRealm
#依賴注入
securityManager.realm=$myRealm
- [users]section 允許你定義一組靜態(tài)的用戶賬戶吗铐。這在大部分擁有少數(shù)用戶賬戶或用戶賬戶不需要再運行時被動地創(chuàng)建的環(huán)境下是很有用的。
[users]
TiaNa=1111
Tim=1111
- [roles]section 允許你把定義在[users]section 中的角色與權(quán)限關(guān)聯(lián)起來杏节。另外唬渗,這在大部分擁有少數(shù)用戶賬戶或用戶賬戶不需要在運行時被動態(tài)地創(chuàng)建的環(huán)境下是很有用的。
[users]
Tim=1111,role1
[roles]
role1=user:add,user:delete
- [urls]很少用
2.使用JdbcRealm來完成身份認證
- 要實現(xiàn)JdbcRealm奋渔,首先要為JdbcRealm設(shè)置datasource
- 在指定的datasource所對應(yīng)的數(shù)據(jù)中應(yīng)用表users镊逝,該表中有username,password嫉鲸,password_salt等字段撑蒜。
3.實現(xiàn)步驟
-
新建數(shù)據(jù)庫表,表名必須是users玄渗,這相當于配置文件的[users]座菠。
數(shù)據(jù)庫表users - 添加依賴
<!--jdk版本-->
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--單元測試-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.3.2</version>
</dependency>
<!--連接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.11</version>
</dependency>
<!--數(shù)據(jù)庫驅(qū)動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
</dependencies>
- 配置shiro.ini文件
[main]
#配置數(shù)據(jù)源
dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass=com.mysql.jdbc.Driver
dataSource.jdbcUrl=jdbc:mysql://localhost:3306/shiro?serverTimezone=GMT%2B8
dataSource.user=root
dataSource.password=123456
#$表示引用對象
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSource
#將 realm 設(shè)置到 securityManager
securityManager.realms=$jdbcRealm
- 編碼測試
public class JdbcRealm {
public static void main(String[] args) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("tiana","123456");
subject.login(token);
try {
subject.login(token);
if(subject.isAuthenticated()){
System.out.println("驗證通過");
}
}catch (AuthenticationException e){
System.out.println("驗證失敗");
}
}
}
四、 自定義Realm
JdbcRealm可以實現(xiàn)從數(shù)據(jù)庫中獲取用戶的驗證信息藤树,但JdbcRealm靈活性太差浴滴。這時就可以通過自定義Realm來實現(xiàn)身份認證功能。
1.Realm接口
- Realm接口定義了根據(jù)token獲取認證信息的方法也榄。Shiro內(nèi)部實現(xiàn)了一系列的Realm巡莹,這些不同的Realm實現(xiàn)類提高了不同的功能。
a) CachingRealm 負責(zé)緩存處理
b) AuthenticationRealm 負責(zé)認證
c) AuthorizingRealm 負責(zé)授權(quán) - 通常自定義的 realm 繼承 AuthorizingRealm甜紫,這樣可以實現(xiàn)身份認證和授權(quán)這兩個自定義方法降宅。
2.實現(xiàn)自定義Realm
- 添加依賴同上一節(jié)
- 自定義UserRealm
public class UserRealm extends AuthorizingRealm {
public String getName(){
return "userRealm";
}
//授權(quán)
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//身份認證
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//獲取用戶名
String username = (String) authenticationToken.getPrincipal();
System.out.println(username);
//根據(jù)用戶名查詢數(shù)據(jù)庫密碼信息--模擬
//假定從數(shù)據(jù)庫獲取的密碼為123456
String pwd = "123456";
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, pwd,getName());
return info;
}
}
- 配置文件
[main]
userRealm=com.shiro.realm.UserRealm
securityManager.realms=$userRealm
- 編碼測試
public class JdbcRealm {
public static void main(String[] args) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("tiana","123456");
subject.login(token);
try {
subject.login(token);
if(subject.isAuthenticated()){
System.out.println("驗證通過");
}
}catch (AuthenticationException e){
System.out.println("驗證失敗");
}
}
}
注意:使用Shiro來完成權(quán)限管理,Shiro不會去維護數(shù)據(jù)囚霸。Shiro中使用的數(shù)據(jù)腰根,需要我們根據(jù)業(yè)務(wù)將數(shù)據(jù)傳遞給Shiro的相應(yīng)的接口。
五 拓型、 AuthticationStrategy:認證策略
- 通過上面的學(xué)習(xí)额嘿,我們知道在Realm大于1時才會使用認證策略瘸恼。
- Shiro有3個具體的AuthticationStrategy實現(xiàn):
a) FirstSuccessfulStrategy:只要有一個Realm驗證成功即可,只返回第一個Realm身份驗證成功的認證信息册养,其他的忽略东帅;
b) AtLeastOneSuccessfulStrategy,只要有一個Realm驗證成功即可球拦,和FirstSuccessfulStrategy不同靠闭,返回所有Realm身份驗證成功的認證信息;
c) AllSuccessfulStrategy坎炼,所有Realm驗證成功才算成功愧膀,且返回所有Realm身份驗證成功的認證信息,如果有一個失敗就失敗了谣光。 -
默認的策略是AtLeastOneSuccessfulStrategy冤议,可以通過修改shiro.ini文件配置驗證策略野瘦。
AuthticationStrategy
六腾节、 散列算法
在身份認證過程中會涉及到加密.Shrio實現(xiàn)了比較多的散列算法.如MD5,SHA等,并且提供了加鹽功能.比如"1111"的MD5碼為"e10adc3949ba59abbe56e057f20f883e",這個MD5可以很容易破解出來,但是如果是姓名+"123456"的組合,那么找到原密碼的難度就會增加.
1.測試MD5案例
public class Md5 {
public static void main(String[] args) {
//使用MD5加密算法加密
Md5Hash md5 = new Md5Hash("123456");
System.out.println(md5.toString());
//加鹽
md5 = new Md5Hash("123456", "sxt");
System.out.println(md5.toString());
//迭代次數(shù)
md5 = new Md5Hash("123456", "sxt",2);
System.out.println(md5.toString());
//與md5 = new Md5Hash("123456", "sxt", 2)加密結(jié)果一致
SimpleHash hash = new SimpleHash("md5", "123456", "sxt", 2);
System.out.println(hash);
}
}
2.在自定義的Realm中使用散列算法
- 添加依賴和測試代碼同上
- 自定義Realm
public class UserRealm extends AuthorizingRealm {
@Override
public String getName() {
return "UserRealm";
}
//用于認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//從 token 中獲取身份信息
String username = (String) token.getPrincipal();
//根據(jù)用戶名到數(shù)據(jù)庫中取出用戶信息 如果查詢不到 返回 null
//按照固定規(guī)則加密碼結(jié)果 ,此密碼要在數(shù)據(jù)庫存儲,原始密碼 是123456, 鹽是 sxt, 2 次散列
//假如從數(shù)據(jù)庫中獲取密碼為 123456
String password = "8a6ed67b1f4fc6f08b1a94f3dae89f60";
//返回認證信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password,
ByteSource.Util.bytes("sxt"), this.getName());
return simpleAuthenticationInfo;
}
//用于授權(quán)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}
- shiro.ini配置
[main]
#定義憑證匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次數(shù)
credentialsMatcher.hashIterations=2
#將憑證匹配器設(shè)置到 realm
userRealm=com.shiro.UserRealm
userRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$userRealm
七 洁桌、授權(quán)
1.基本概念
-
授權(quán)
也叫訪問控制,即在應(yīng)用中控制誰能訪問哪些資源(如訪問頁面/編輯數(shù)據(jù)/頁面操作等)。在授權(quán)中需了解的幾個關(guān)鍵對象:主體(Subject)捡絮、資源(Resource)熬芜、權(quán)限(Permission)、角色(Role)福稳。
授權(quán) - 權(quán)限粒度
分為粗粒度和細粒度涎拉。
a)粗粒度:對user的crud,也就是對表的操作。
b)細粒度:是對記錄的操作的圆。如,只允許查詢id為1的user的工資鼓拧。
c)Shiro一般管理的是粗粒度的權(quán)限。比如:菜單,按鈕,url越妈。一般細粒度的權(quán)限是通過業(yè)務(wù)來控制的季俩。 - 角色
權(quán)限的集合。一般情況下我們會賦予用戶角色而不是權(quán)限,即這樣用戶可以擁有一組權(quán)限,賦予權(quán)限時比較方便梅掠。 - 權(quán)限
表示規(guī)則--資源:操作:實例酌住。可以用通配符表示阎抒。
如,user:add表示對user有添加的權(quán)限酪我。
user:*表示對user具有所有操作的權(quán)限。
user:delete:100表示對user標識為100的記錄有刪除的權(quán)限且叁。
2.授權(quán)流程
- 首先調(diào)用 Subject.isPermitted/hasRole接口,其會委托給 SecurityManager,SecurityManager 接著會委托給 Authorizer;
- Authorizer 是真正的授權(quán)者,如果我們調(diào)用如 isPermitted(“user:view”),其首先會通過PermissionResolver 把字符串轉(zhuǎn)換成相應(yīng)的 Permission 實例;
- 在進行授權(quán)之前,其會調(diào)用相應(yīng)的 Realm 獲取 Subject 相應(yīng)的角色/權(quán)限用于匹配傳入的角色/權(quán)限;
- Authorizer 會判斷 Realm 的角色/權(quán)限是否和傳入的匹配,如果有多個 Realm,會委托給
ModularRealmAuthorizer 進行循環(huán)判斷,如果匹配如 isPermitted/hasRole會返回 true,否
則返回 false 表示授權(quán)失敗都哭。
3.編碼實現(xiàn)
- shiro.ini
[users]
tiana=123456,role1
tia=123456,role2
[roles]
role1=user:add,user:update,user:delete
role2=user:*
- 編碼測試
public static void main(String[] args) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("tiana", "123456");
try {
//認證
subject.login(token);
} catch (AuthenticationException e) {
System.out.println("認證不通過");
}
//基于角色的授權(quán)
boolean role1 = subject.hasRole("role1");
System.out.println(role1);
//判斷是否具有多個角色
subject.hasRole("role1,role2");
//可以通過checkRole來檢測是否具有某個角色,如果不具有會拋出異常
subject.checkRole("role1");
//也可以同時檢測多個角色
subject.checkRole("role1,role2");
//基于資源的授權(quán)
boolean flag = subject.isPermitted("user:delete");
System.out.println(flag);
//判斷是否具有多個權(quán)限
subject.isPermittedAll("user:add", "user:update");
//checkPermission檢測認證用戶是否具有某個權(quán)限,如果沒有拋出異常
subject.checkPermission("user:add");
}
4.Shiro 支持三種方式的授權(quán)
- 編程式
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有權(quán)限
} else {
//無權(quán)限
}
- 注解式,在 JSP/GSP 頁面通過相應(yīng)的標簽完成
@RequiresRoles("admin")
public void hello() {
//有權(quán)限
}
- 標簽式,在 JSP/GSP 頁面通過相應(yīng)的標簽完成
<shiro:hasRole name="admin">
<!— 有權(quán)限—>
</shiro:hasRole>
5.自定義Realm實現(xiàn)授權(quán)
僅僅通過配置文件來指定權(quán)限不夠靈活,并且不方便.在實際應(yīng)用的大多數(shù)情況下都是將用戶信息,角色信息,權(quán)限信息保存到了數(shù)據(jù)庫中.所有需要從數(shù)據(jù)庫中獲取相關(guān)的數(shù)據(jù)信息.可以使用Shiro提供的JdbcRealm來實現(xiàn),也可以自定義Realm實現(xiàn),但在實際中一般是自定義Realm實現(xiàn).
- 自定義Realm
public class UserRealm extends AuthorizingRealm {
@Override
public String getName() {
return "UserRealm";
}
//用于授權(quán)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = principals.getPrimaryPrincipal().toString();
System.out.println(username);
//根據(jù)用戶名查詢用戶對應(yīng)的權(quán)限信息--模擬
ArrayList<String> permissions = new ArrayList<>();
permissions.add("user:add");
permissions.add("user:delete");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for (String permission:permissions){
info.addStringPermission(permission);
}
return info;
}
- shiro.ini
[main]
userRealm=com.shiro.authc.UserRealm
securityManager.realm=$userRealm
- 測試代碼
public static void main(String[] args) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("tiana", "123456");
try {
//認證
subject.login(token);
} catch (AuthenticationException e) {
System.out.println("認證不通過");
}
System.out.println(subject.isPermittedAll("user:add","user:delete"));
}
springboot整合shiro網(wǎng)上有很多,這里就不再贅述。