天天看點

Shiro架構01(Shiro簡介、環境搭建、異常分析、認證流程、JdbcRealm、認證政策)

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中的體系組成
Shiro架構01(Shiro簡介、環境搭建、異常分析、認證流程、JdbcRealm、認證政策)

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整體架構
Shiro架構01(Shiro簡介、環境搭建、異常分析、認證流程、JdbcRealm、認證政策)

Subject:(org.apache.shiro.subject.Subject)

目前與軟體進行互動的實體(使用者,第三方服務程式,cron job,等等)的安全特定“視圖”。

SecurityManager:SecurityManager 是 Shiro 架構的心髒。它基本上是一個“保護傘”對象,協調其管理的元件以確定它們能夠一起順利的工作。

Realms:域

Realms 在 Shiro 和你的應用程式的安全資料之間擔當“橋梁”或“連接配接器”。當它實際上與安全相關的資料如用來執行身份驗證(登入)及授權(通路控制)的使用者帳戶互動時,Shiro 從一個或多個為應用程式配置的 Realm 中尋找許多這樣的東西。

2.Shiro環境的搭建

A、導包

Shiro架構01(Shiro簡介、環境搭建、異常分析、認證流程、JdbcRealm、認證政策)

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指派。

作用:用于連接配接資料源(資料庫)

Shiro架構01(Shiro簡介、環境搭建、異常分析、認證流程、JdbcRealm、認證政策)

A、mysql中的使用者表如下所示

Shiro架構01(Shiro簡介、環境搭建、異常分析、認證流程、JdbcRealm、認證政策)

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表

Shiro架構01(Shiro簡介、環境搭建、異常分析、認證流程、JdbcRealm、認證政策)

資料源2—>shiro2資料庫中的users表

Shiro架構01(Shiro簡介、環境搭建、異常分析、認證流程、JdbcRealm、認證政策)

分析可知:兩個資料源中有相同的資料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

繼續閱讀