Shiro學(xué)習(xí)筆記

一 、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.基本功能
Shiro基本功能.png
  • 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 完成工作:
Shiro架構(gòu).png
  • 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)
Shiro架構(gòu).png
  • 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,驗證用戶是否合法功茴。


Authentication
  • 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)流程
授權(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)上有很多,這里就不再贅述。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末欺矫,一起剝皮案震驚了整個濱河市纱新,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌穆趴,老刑警劉巖脸爱,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異毡代,居然都是意外死亡阅羹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門教寂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人执庐,你說我怎么就攤上這事酪耕。” “怎么了轨淌?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵迂烁,是天一觀的道長。 經(jīng)常有香客問我递鹉,道長盟步,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任躏结,我火速辦了婚禮却盘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘媳拴。我一直安慰自己黄橘,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布屈溉。 她就那樣靜靜地躺著塞关,像睡著了一般。 火紅的嫁衣襯著肌膚如雪子巾。 梳的紋絲不亂的頭發(fā)上帆赢,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音线梗,去河邊找鬼椰于。 笑死,一個胖子當著我的面吹牛缠导,可吹牛的內(nèi)容都是我干的廉羔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼憋他!你這毒婦竟也來了孩饼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤竹挡,失蹤者是張志新(化名)和其女友劉穎镀娶,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體揪罕,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡梯码,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了好啰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轩娶。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖框往,靈堂內(nèi)的尸體忽然破棺而出鳄抒,到底是詐尸還是另有隱情,我是刑警寧澤椰弊,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布许溅,位于F島的核電站,受9級特大地震影響秉版,放射性物質(zhì)發(fā)生泄漏贤重。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一清焕、第九天 我趴在偏房一處隱蔽的房頂上張望并蝗。 院中可真熱鬧,春花似錦耐朴、人聲如沸借卧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铐刘。三九已至,卻和暖如春影晓,著一層夾襖步出監(jiān)牢的瞬間镰吵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工挂签, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留疤祭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓饵婆,卻偏偏與公主長得像勺馆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354