這系列的文章目的是讓懂一點Java編程經驗的人能夠迅速的入門並且掌握shiro這類安全框架的使用。所以我們不講原理,也不談shiro的具體內部實現,我們的目標是在有限的時間內,迅速的掌握shiro框架並且學以致用,不會過多的談及這麼做的原因以及其背後的原理。廢話不多說,我希望能直達我們的痛點,直接解決我們的目前的問題。
問題:如何利用shiro框架達到認證和授權的目的?作爲一個安全的框架,認證和授權是兩大組成部分,認證是爲了證明我是誰,授權則是我能有什麼可操作的權限。直接上代碼。
目錄
6. 在shiroTest.java的同名文件夾下新建shiro.ini文件用於存儲用戶名和密碼等信息
8.1 現在很多博客或者培訓機構的課程仍然在用過時的這段代碼來初始化,這很誤導了人,讓人浪費了不少時間。
2.1 在1的基礎上,我們更改shiro.ini文件,加入以下的內容來表示有一定的角色和權限
2.2 先驗證用戶是否成功登陸,也即驗證用戶是否認證成功,shiro認爲授權發生在認證成功之後,否則,沒有授權一說。
2.3 我們根據在shiro.ini填寫的代碼可以知道,用戶zhangsan有admin角色,並且對應有add和update的操作權限,那麼我們來寫代碼測試一下是否根據文件所說具有對應角色和權限
1. 如何使用shiro中的iniRealm來認證
1. eclipse創建maven項目
如圖所示
maven項目命名
2. 在pom.xml文件中引入shiro相關maven庫
在dependencies節點下插入以下的代碼片段
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
3. 在src文件夾下建立shiro包
如圖所示
4. 在shiro包下新建shiroTest.java文件
5. shiroTest.java文件代碼如下
public class ShiroTest {
@Test
public void Test() {
// 創建默認SecurityManager用於管理security
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
// 創建IniRealm對象用於從ini文件中讀取信息
IniRealm iniRealm = new IniRealm("classpath:shiro/shiro.ini");
// 設置iniRealm到默認的SecurityManager
defaultSecurityManager.setRealm(iniRealm);
// 使用securityUtils工具類裝載默認的securitymanager
SecurityUtils.setSecurityManager(defaultSecurityManager);
// 從securityUtils獲取對應的subject對象
Subject subject = SecurityUtils.getSubject();
// 設置登錄的用戶名和密碼令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("zhangsan", "123");
// 使用subject.login模擬登陸過程
subject.login(usernamePasswordToken);
}
}
6. 在shiroTest.java的同名文件夾下新建shiro.ini文件用於存儲用戶名和密碼等信息
[users]
zhangsan=123
wangwu=456
7. 編譯運行shiroTest.java文件,效果圖如下
說明了測試用例通過,shiro完成了模擬登陸的過程
8. 注意事項
8.1 現在很多博客或者培訓機構的課程仍然在用過時的這段代碼來初始化,這很誤導了人,讓人浪費了不少時間。
//加載配置文件,並獲取工廠
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//獲取安全管理者實例
SecurityManager sm = factory.getInstance();
首先說明,自shiro1.2.x以上的版本,iniSecurityManagerFactory工廠模式的代碼就已經被廢棄了,至於爲什麼,暫時不在這篇文章討論,若使用shiro1.2.x以上的版本,請採用文章中的IniRealm代碼。
8.2 關於subject.login加異常處理塊的問題
我希望我們能夠反向思考一個問題
8.2.1 當我們在token中填寫的用戶名或者密碼在ini文件中不存在時,程序會報什麼異常?
8.2.2 當我們在token中填寫的用戶名在ini文件中存在,但對應填寫的密碼和ini文件密碼不符合時,程序會報什麼異常?
8.2.3 當我們在token中填寫的密碼在ini存在,但對應填寫的用戶名和ini不符合時,程序會報什麼異常?
我希望我們能夠多思考一些,通過程序報出的異常錯誤反過來推,錯誤的地方在哪裏,這樣有利於我們很好的快速的debug
爲什麼本該在subject.login代碼加try-catch塊我沒有加上,原因就是以上。我希望大家看到這裏,能夠暫停一下,多多思考一下,從錯誤地方推異常,從異常報告推錯誤地方。
答案揭曉
8.2.1的用戶名或者密碼不存在的問題,程序報如下異常
大家可以看到程序報UnKnownAccountException異常,意思就是未知的賬戶異常。
8.2.2的密碼不對的問題,程序報如下異常
大家可以看到程序報IncorrectCredentialsException異常,意思是錯誤的認證異常,表示密碼不對
8.2.3的用戶名不對問題,程序報如下異常
大家可以看到情況和8.2.1的一致都是報未知用戶的異常
8.3 以上的異常處理僅僅是爲了讓大家思考在不同的錯誤情況下,程序會發生什麼樣的情況,然後根據異常報告,能夠快速的debug。爲了代碼風格和標準,都應該在subject.login處加異常塊,如下代碼段所示
// 設置try-catch塊捕捉驗證login登錄異常
try {
subject.login(usernamePasswordToken);
System.out.println("shiro驗證登錄成功");
} catch (UnknownAccountException uae) {
System.out.println("未知的賬戶名");
} catch (IncorrectCredentialsException ice) {
System.out.println("密碼錯誤");
} catch (Exception e) {
System.out.println("shiro驗證異常");
}
2. 如何使用shiro中的iniRealm來授權
2.1 在1的基礎上,我們更改shiro.ini文件,加入以下的內容來表示有一定的角色和權限
//表示用戶名=密碼,角色
[users]
zhangsan=123,admin
wangwu=456,adminplus
//表示角色=權限
[roles]
admin=add,update
adminplus=add,update,delelte,get
然後我們更改shiroTest.java文件
2.2 先驗證用戶是否成功登陸,也即驗證用戶是否認證成功,shiro認爲授權發生在認證成功之後,否則,沒有授權一說。
// 驗證當前用戶是否登錄成功
System.out.println(subject.getPrincipal() + "是否登錄成功?" + subject.isAuthenticated());
// 登出當前用戶
subject.logout();
// 驗證當前用戶是否登出
System.out.println(subject.getPrincipal() + "是否登錄成功?" + subject.isAuthenticated());
代碼如上,直接在1的基礎上,在文件後添加以上代碼就好,運行測試得到如下結果就表示用戶已經通過認證了。
2.3 我們根據在shiro.ini填寫的代碼可以知道,用戶zhangsan有admin角色,並且對應有add和update的操作權限,那麼我們來寫代碼測試一下是否根據文件所說具有對應角色和權限
先是測試角色代碼
// 使用ArrayList存儲角色
List<String> roles=new ArrayList<String>();
roles.add("admin");
roles.add("adminplus");
roles.add("adminpro");
// 循環測試角色
for(String role:roles) {
System.out.println("當前用戶是"+subject.getPrincipal()+"\t 是否具有"+role+"角色 \t"+subject.hasRole(role));
}
運行測試如圖表示角色是否擁有
其次是測試角色所具有的權限代碼
// 使用arraylist存放權限組
List<String> permissions=new ArrayList<String>();
permissions.add("update");
permissions.add("delete");
permissions.add("add");
permissions.add("get");
// 循環驗證當前用戶是否具有對應的權限
for(String permission:permissions)
{
System.out.println("當前用戶是"+subject.getPrincipal()+"\t 是否具有"+permission+"權限 \t"+subject.isPermitted(permission));
}
運行測試如下表示權限是否擁有
3. 總結
3.1 認證的主要核心代碼
創建默認的DefaultSecurityManager對象->創建iniRealm數據源對象->設置iniRealm對象到DafaultSecurityManager管理器->使用SecurityUtils加載DefaultSecurityManager管理器->從SecurityUtils獲取當前的Subject對象->使用UsernamePasswordToken創建用戶名和密碼的token->Subject.login方法進行登錄認證(記得加try-catch塊處理異常)
3.2 授權的主要核心代碼
Subject.getPrinciPal()獲取到當前的用戶名->Subject.isAuthenticated()驗證是否認證通過->Subject.hasRole()驗證是否具有對應的角色->Subject.isPermitted()驗證是否具有對應的權限