Shiro架構01
1.Shiro簡介
[1]為什麼要學習Shiro
A、目前遇到的問題:
使用RABC進行角色通路控制的時候,代碼書寫起來比較麻煩。
目前學習的寫的操作代碼整體不太安全。
B、解決方案:
Spring securing :可以解決以上問題
缺點: 基于Spring之上的,局限性比較大
Shiro :可以用在JavaEE、JavaSE和分布式項目中。
什麼是Shiro
Apache Shiro 是一個強大而靈活的開源安全架構,它幹淨利落地處理身份認證,授權,企業會話管理和加密。
Shior官網:http://shiro.apache.org/
[2]Shiro中的體系組成
A、Authentication:身份驗證,就是我們平時所做的登入。
B、Authorization:授權,賦予角色不同的菜單、功能。
C、Session Management:管理登入使用者的資訊。
D、Cryptography:加密技術。MD5加密算法等。
a、Web Support:Shiro 對 web項目的支援
b、Caching:緩存 可以安全快速的操作
c、Concurrency:Apache Shiro 利用它的并發特性來支援多線程應用程式。
d、Testing:測試。
e、Run As:可以實作一個使用者被允許的情況下,使用另一個使用者通路
f、Remember Me:記住我
[3]Shiro整體架構
Subject:(org.apache.shiro.subject.Subject)
目前與軟體進行互動的實體(使用者,第三方服務程式,cron job,等等)的安全特定“視圖”。
SecurityManager:SecurityManager 是 Shiro 架構的心髒。它基本上是一個“保護傘”對象,協調其管理的元件以確定它們能夠一起順利的工作。
Realms:域
Realms 在 Shiro 和你的應用程式的安全資料之間擔當“橋梁”或“連接配接器”。當它實際上與安全相關的資料如用來執行身份驗證(登入)及授權(通路控制)的使用者帳戶互動時,Shiro 從一個或多個為應用程式配置的 Realm 中尋找許多這樣的東西。
2.Shiro環境的搭建
A、導包
B、書寫shiro.ini檔案(IDEA需要下載下傳Ini插件----關閉防火牆)
[users]
zs=123
sxt=root
C、書寫測試代碼
package cn.qt.shiro1;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class TestA {
public static void main(String[] args) {
//[1]解析Shiro.ini檔案
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//[2]通過SecurityManager工廠獲得SecurityManager執行個體
SecurityManager securityManager = factory.getInstance();
//[3]把SecurityManager對象設定到運作環境中
SecurityUtils.setSecurityManager(securityManager);
//[4]通過SecurityUtils獲得Subject
Subject subject = SecurityUtils.getSubject();
//[5]書寫自己的賬戶和密碼----相當于使用者自己輸入的賬戶和密碼
//我們拿自己書寫的賬戶和密碼去和shiro.ini中的賬戶密碼比較
UsernamePasswordToken token = new UsernamePasswordToken("zs","1223");
//[6]進行身份的驗證
try {
subject.login(token);
//[7]通過方法判斷是否登入成功
if (subject.isAuthenticated()){
System.out.println("登入成功");
}
} catch (UnknownAccountException e) {//使用者名錯誤
System.out.println("使用者名錯誤");
}catch (IncorrectCredentialsException e){//密碼錯誤
System.out.println("密碼錯誤");
}
}
}
3.Shiro中的異常分析
A、賬戶失效異常
DisabledAccountException
B、競争次數過多-----賬戶和密碼在多地同時登入
ConcurrentAccessException
C、嘗試次數過多
ExcessiveAttemptsException
D、使用者名不正确
UnknownAccountException
E、憑證不正确----密碼不正确
IncorrectCredentialsException
F、憑證過期
ExpiredCredentialsException
使用Shiro做異常處理的時候,盡量把異常資訊表示的婉轉一點,比如使用者名錯誤或者是密碼錯誤,給出的提示資訊均為“使用者名或密碼錯誤”,而不是單獨把“使用者名錯誤”或“密碼錯誤”提示給使用者。這樣做有助于提升代碼的安全性(防止使用者惡意嘗試登陸)。
4.Shiro的認證流程
A、通過 shiro 相關的 API 建立了 SecurityManager 以及獲得
subject 執行個體
B、封裝了 token 資訊
C、較長的描述
通過 subject.login(token)進行使用者認證
Subject 接受 token 資訊 ,通過 DelegatingSubject
将 token 委托給 securityManager 完成認證
securityManager 通過使用 DefaultSecurityManager
完成相關功能由 DefaultSecurityManager 中的 login 方法完
成對應的認證在 login 中調用了
AuthenticatingSecurityManager
中的 authenticate 方法完成認證
使用其中的doAuthenticate 獲得realms資訊如果是單個直接進
行比較,判斷是否成功,如果是多個 raalm 需要使用驗證政策完成
對應的認證工作
5.Shiro中的JdbcRealm
jdbcRealm中封裝好了查詢語句,使用JDBC進行資料的查詢操作。但是由于資料源不确定,即不知道開發者用的是mysql、oracle還是其他的資料庫。是以在jdbcRealm中并沒有dataSource指派,需要在ini檔案中配置資料源,通過掃描解析ini檔案中的内容給dataSource指派。
作用:用于連接配接資料源(資料庫)
A、mysql中的使用者表如下所示
B、這裡我們用c3p0連接配接資料源,ini檔案如下所示
[main]
#獲得資料源
dataSou=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou.driverClass=com.mysql.jdbc.Driver
dataSou.jdbcUrl=jdbc:mysql://localhost:3306/shiro
dataSou.user=root
dataSou.password=1111
#配置jdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSou
#設定securityManager中的realm
securityManager.realm=$jdbcRealm
C、測試代碼如下所示
package cn.qt.shiro1;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class TestB {
public static void main(String[] args) {
//[1]解析Shiro.ini檔案
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro-jdbc.ini");
//[2]通過SecurityManager工廠獲得SecurityManager執行個體
SecurityManager securityManager = factory.getInstance();
//[3]把SecurityManager對象設定到運作環境中
SecurityUtils.setSecurityManager(securityManager);
//[4]通過SecurityUtils獲得Subject
Subject subject = SecurityUtils.getSubject();
//[5]書寫自己的賬戶和密碼----相當于使用者自己輸入的賬戶和密碼
//我們拿自己書寫的賬戶和密碼去和shiro.ini中的賬戶密碼比較
UsernamePasswordToken token = new UsernamePasswordToken("zs","123");
//[6]進行身份的驗證
try {
subject.login(token);
//[7]通過方法判斷是否登入成功
if (subject.isAuthenticated()){
System.out.println("登入成功");
}
} catch (UnknownAccountException e) {//使用者名錯誤
System.out.println("使用者名錯誤");
}catch (IncorrectCredentialsException e){//密碼錯誤
System.out.println("密碼錯誤");
}
}
}
在實際的項目中資料庫表名和資料庫字段名一般不會和JdbcRealm類中封裝的語句保持一緻,這就需要我們自定義realm.
6.認證政策
規定了如果有多個資料源時應該如何操作
A、AtLeastOneSuccessfulStrategy:
如果一個(或更多)Realm 驗證成功,則整體的嘗試被認為是成功的。如果沒有一個驗證成功,則整體嘗試失敗。類似于java中的 |
如果ini檔案中培養配置認證政策,則預設的認證政策為該政策
B、FirstSuccessfulStrategy:
隻有第一個成功地驗證的 Realm 傳回的資訊将被使用。所有進一步的 Realm 将被忽略。如果沒有一個驗證成功,則整體嘗試失敗。 類似于java中的||
C、AllSucessfulStrategy:
為了整體的嘗試成功,所有配置的 Realm 必須驗證成功。如果沒有一個驗證成功,則整體嘗試失敗。
在ini檔案中中配置政策
1.資料源
資料源1---->shiro資料庫中的users表
資料源2—>shiro2資料庫中的users表
分析可知:兩個資料源中有相同的資料zs—123,有不同的資料:資料源1中lisi—123,而資料源2中則是ww—123
ini檔案如下
[main]
#獲得資料源1
dataSou=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou.driverClass=com.mysql.jdbc.Driver
dataSou.jdbcUrl=jdbc:mysql://localhost:3306/shiro
dataSou.user=root
dataSou.password=1111
#配置jdbcRealm1
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSou
#獲得資料源2
dataSou2=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou2.driverClass=com.mysql.jdbc.Driver
dataSou2.jdbcUrl=jdbc:mysql://localhost:3306/shiro2
dataSou2.user=root
dataSou2.password=1111
#配置jdbcRealm2
jdbcRealm2=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm2.dataSource=$dataSou2
#配置驗證器
authenticationStrategy= org.apache.shiro.authc.pam.FirstSuccessfulStrategy
#設定securityManager中的realm
securityManager.realms=$jdbcRealm,$jdbcRealm2
securityManager.authenticator.authenticationStrategy=$authenticationStrategy
測試檔案TestA
package cn.qt.shiro2;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class TestA {
public static void main(String[] args) {
//[1]解析Shiro.ini檔案
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro-jdbc.ini");
//[2]通過SecurityManager工廠獲得SecurityManager執行個體
SecurityManager securityManager = factory.getInstance();
//[3]把SecurityManager對象設定到運作環境中
SecurityUtils.setSecurityManager(securityManager);
//[4]通過SecurityUtils獲得Subject
Subject subject = SecurityUtils.getSubject();
//[5]書寫自己的賬戶和密碼----相當于使用者自己輸入的賬戶和密碼
//我們拿自己書寫的賬戶和密碼去和shiro.ini中的賬戶密碼比較
UsernamePasswordToken token = new UsernamePasswordToken("ww","123");
//[6]進行身份的驗證
try {
subject.login(token);
//[7]通過方法判斷是否登入成功
if (subject.isAuthenticated()){
System.out.println("登入成功");
}
} catch (UnknownAccountException e) {//使用者名錯誤
System.out.println("使用者名錯誤");
}catch (IncorrectCredentialsException e){//密碼錯誤
System.out.println("密碼錯誤");
}
}
}
測試結果:
A、驗證器:AtLeastOneSuccessfulStrategy或FirstSuccessfulStrategy
登入時:無論是登入zs、lisi還是ww,結果均為登入成功。
結論:AtLeastOneSuccessfulStrategy和FirstSuccessfulStrategy兩種驗證方式結果相同,即多個資料源中,隻要有一個資料源傳回登入成功,結果就為成功。兩者的差別是:AtLeastOneSuccessfulStrategy相當于java中的 |,所有項都會執行,FirstSuccessfulStrategy則相當于java中的 || ,隻要有一個為true,則直接傳回true,後續的項不再判斷。
B、驗證器AllSucessfulStrategy
登入時:登入zs,則登入成功,lisi或ww則顯示使用者名錯誤
結論:必須所有的資料源都含有zs這條資料才傳回true