Apache shiro筆記 (1)入門

參考:

https://jinnianshilongnian.iteye.com/blog/2018936

http://how2j.cn/k/shiro/shiro-tutorial/1720.html#nowhere

RBAC 是當下權限系統的設計基礎,同時有兩種解釋:
1.: Role-Based Access Control,基於角色的訪問控制
即,你要能夠刪除產品,那麼當前用戶就必須擁有產品經理這個角色
2.:Resource-Based Access Control,基於資源的訪問控制
即,你要能夠刪除產品,那麼當前用戶就必須擁有刪除產品這樣的權限

開源權限管理項目:

Spring Security   

Apache Shiro     

 

Apache Shiro是Java的一個安全框架,基本功能點如圖:

Authentication身份認證/登錄,驗證用戶是不是擁有相應的身份;

Authorization授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能做事情,常見的如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具有某個權限;

Session Manager會話管理,即用戶登錄後就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通JavaSE環境的,也可以是如Web環境的;

Cryptography加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;

Web SupportWeb支持,可以非常容易的集成到Web環境;

Caching:緩存,比如用戶登錄後,其用戶信息、擁有的角色/權限不必每次去查,這樣可以提高效率;

Concurrencyshiro支持多線程應用的併發驗證,即如在一個線程中開啓另一個線程,能把權限自動傳播過去;

Testing提供測試支持;

Run As允許一個用戶假裝爲另一個用戶(如果他們允許)的身份進行訪問;

Remember Me記住我,這個是非常常見的功能,即一次登錄後,下次再來的話不用登錄了。

以用戶登錄爲例子,

 

應用代碼直接交互的對象是Subject,也就是說Shiro的對外API核心就是Subject;其每個API的含義:

Subject主體,代表了當前“用戶”,這個用戶不一定是一個具體的人,與當前應用交互的任何東西都是Subject,如網絡爬蟲,機器人等;即一個抽象概念;所有Subject都綁定到SecurityManager,與Subject的所有交互都會委託給SecurityManager;可以把Subject認爲是一個門面;SecurityManager纔是實際的執行者;

SecurityManager安全管理器;即所有與安全有關的操作都會與SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它負責與後邊介紹的其他組件進行交互,如果學習過SpringMVC,你可以把它看成DispatcherServlet前端控制器;

Realm域,Shiro從從Realm獲取安全數據(如用戶、角色、權限),就是說SecurityManager要驗證用戶身份,那麼它需要從Realm獲取相應的用戶進行比較以確定用戶身份是否合法;也需要從Realm得到用戶相應的角色/權限進行驗證用戶是否能進行操作;可以把Realm看成DataSource,即安全數據源。

最簡單的一個Shiro應用:

1、應用代碼通過Subject來進行認證和授權,而Subject又委託給SecurityManager;

2、我們需要給Shiro的SecurityManager注入Realm,從而讓SecurityManager能得到合法的用戶及其權限進行判斷。

一.接下來入門一個通過ini的簡單登錄的例子:

1.使用idea創建一個普通的java項目  

新手可以參照:https://blog.csdn.net/oschina_41790905/article/details/79475187

2.引入兩個jar包 

   新手關於jar包引入可以參考https://blog.csdn.net/qq_28289405/article/details/82216847

3.在src目錄下新建 shiro.ini
 用戶數據  此處充當realm域
 

#定義用戶
[users]
#用戶名 zhang3  密碼是 12345
zhang3 = 12345
#用戶名 li4  密碼是 abcde
li4 = abcde

3. 在src目錄下創建 包結構,創建User.class

public class User {


    private String name;
    private String password;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

 

4. 創建 TestShiro

準備3個用戶,前兩個能在 shiro.ini 中找到,第3個不存在
然後測試登錄

public class TestShiro {
    public static void main(String[] args) {
        User zhang3 = new User();
        zhang3.setName("zhang3");
        zhang3.setPassword("12345");

        User li4 = new User();
        li4.setName("li4");
        li4.setPassword("abcde");

        User wang5 = new User();
        wang5.setName("wang5");
        wang5.setPassword("wrongpassword");
        List<User> users = new ArrayList<>();
        users.add(zhang3);
        users.add(li4);
        users.add(wang5);

      
        //登陸每個用戶
        for (User user : users) {

            if(login(user))
                System.out.printf("%s \t成功登陸,用的密碼是 %s\t %n",user.getName(),user.getPassword());
            else
               System.out.printf("%s \t成功失敗,用的密碼是 %s\t %n",user.getName(),user.getPassword());
        }

    }

    private static boolean login(User user) {
        Subject subject = getSubject(user);
        //如果已經登錄過了,退出
        if (subject.isAuthenticated())
            subject.logout();
        //封裝用戶的數據
//token可以理解爲用戶令牌,登錄的過程被抽象爲Shiro驗證令牌是否具有合法身份以及相關權限。
        UsernamePasswordToken token = new UsernamePasswordToken(user.getName(),user.getPassword());
        try {
            //將用戶的數據token 最終傳遞到Realm中進行對比
            subject.login(token);
        } catch (AuthenticationException e) {
            //驗證錯誤
            return false;
        }
        return subject.isAuthenticated();

    }

    private static Subject getSubject(User user) {
        //加載配置文件,並獲取工廠
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

        //獲取安全管理者實例
        SecurityManager sm = factory.getInstance();

        //將安全管理者放入全局對象,注入SecurityManager
        SecurityUtils.setSecurityManager(sm);

        //全局對象通過安全管理者生成Subject對象
        Subject subject = SecurityUtils.getSubject();

        return subject;
    }


}

5.  運行查看結果

二、通過數據庫充當realm域

1.創建表

前面提到過,RBAC 的概念, RBAC會存在3 張基礎表: 用戶,角色,權限, 以及 2 張中間表來建立 用戶與角色的多對多關係,角色與權限的多對多關係。 用戶與權限之間也是多對多關係,但是是通過 角色間接建立的。

 

2.  添加jar包

簡單介紹一下幾個jar包:

commons-beanutils是Apache開源組織提供的用於操作JAVA BEAN的工具包。使用commons-beanutils,我們可以很方便的對bean對象的屬性進行操作。參考:https://blog.csdn.net/jianggujin/article/details/51104949

slf4j:Simple Logging Facade for Java,爲java提供的簡單日誌Facade。Facade門面,更底層一點說就是接口。它允許用戶以自己的喜好,在工程中通過slf4j接入不同的日誌系統。 參考:https://www.cnblogs.com/lujiango/p/8573411.html

因此slf4j入口就是衆多接口的集合,它不負責具體的日誌實現,只在編譯時負責尋找合適的日誌系統進行綁定。具體有哪些接口,全部都定義在slf4j-api中。它只提供一個核心slf4j api(就是slf4j-api.jar包),這個包只有日誌的接口,並沒有實現,所以如果要使用就得再給它提供一個實現了些接口的日誌包,比 如:log4j,common logging,jdk log日誌實現包等。

Jakarta  Commons-logging(JCL)是apache最早提供的日誌的門面接口。提供簡單的日誌實現以及日誌解耦功能。                ---------------------------------------------------------------------------https://www.cnblogs.com/xiadongqing/p/5208824.htm

mysql-connector-java-5.0.8-bin JDBC驅動

3. 

public class DatabaseRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //能進入到這裏,表示賬號已經通過驗證了
        String userName =(String) principalCollection.getPrimaryPrincipal();
        //通過DAO獲取角色和權限
        Set<String> permissions = new DAO().listPermissions(userName);
        Set<String> roles = new DAO().listRoles(userName);

        //授權對象
        SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
        //把通過DAO獲取到的角色和權限放進去
        s.setStringPermissions(permissions);
        s.setRoles(roles);
        return s;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //獲取賬號密碼
        UsernamePasswordToken t = (UsernamePasswordToken) token;
        String userName= token.getPrincipal().toString();
        String password= new String( t.getPassword());
        //獲取數據庫中的密碼
        String passwordInDB = new DAO().getPassword(userName);

        //如果爲空就是賬號不存在,如果不相同就是密碼錯誤,但是都拋出AuthenticationException,而不是拋出具體錯誤原因,免得給破解者提供幫助信息
        if(null==passwordInDB || !passwordInDB.equals(password))
            throw new AuthenticationException();

        //認證信息裏存放賬號密碼, getName() 是當前Realm的繼承方法,通常返回當前類名 :databaseRealm
        SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(userName,password,getName());
        return a;
    }
}

4.修改ini文件

[main]
databaseRealm=com.jxw.DatabaseRealm
securityManager.realms=$databaseRealm

5. 運行結果與之前相同

Maven項目入門

 

身份驗證,即在應用中誰能證明他就是他本人。一般提供如他們的身份ID一些標識信息來表明他就是他本人,如提供身份證,用戶名/密碼來證明。

在shiro中,用戶需要提供principals (身份)和credentials(證明)給shiro,從而應用能驗證用戶身份:

principals:身份,即主體的標識屬性,可以是任何東西,如用戶名、郵箱等,唯一即可。一個主體可以有多個principals,但只有一個Primary principals,一般是用戶名/密碼/手機號。

credentials:證明/憑證,即只有主體知道的安全值,如密碼/數字證書等。

最常見的principals和credentials組合就是用戶名/密碼了。接下來先進行一個基本的身份認證。

另外兩個相關的概念是之前提到的SubjectRealm,分別是主體及驗證主體的數據源。

基本的身份認證

1. 通過idea創建一個maven項目

項目結構如圖

 

 

2. 在pom.xml中添加如下的代碼,引入相關需要的jar包

<dependencies>  
    <dependency>  
        <groupId>junit</groupId>  
        <artifactId>junit</artifactId>  
        <version>4.9</version>  
    </dependency>  
    <dependency>  
        <groupId>commons-logging</groupId>  
        <artifactId>commons-logging</artifactId>  
        <version>1.1.3</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.shiro</groupId>  
        <artifactId>shiro-core</artifactId>  
        <version>1.2.2</version>  
    </dependency>  
</dependencies>

3.創建shiro.ini  用戶身份/憑據

通過[users]指定了兩個主體:zhang/123、wang/123。

即zhang爲用戶名 123位密碼

[users]
zhang=123
wang=123

4.編寫測試用例 LoginTest.java

public class LoginTest {
    @Test
    public  void test1(){
        //    1、首先通過new IniSecurityManagerFactory並指定一個ini配置文件來創建一個SecurityManager工廠;
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//    2、接着獲取SecurityManager並綁定到SecurityUtils,這是一個全局設置,設置一次即可;
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
//    3、通過SecurityUtils得到Subject,其會自動綁定到當前線程;如果在web環境在請求結束時需要解除綁定;然後獲取身份驗證的Token,如用戶名/密碼;
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("zhang", "1");
//    4、調用subject.login方法進行登錄,其會自動委託給SecurityManager.login方法進行登錄;
//    5、如果身份驗證失敗請捕獲AuthenticationException或其子類,常見的如: DisabledAccountException(禁用的帳號)、
// LockedAccountException(鎖定的帳號)、UnknownAccountException(錯誤的帳號)、
// ExcessiveAttemptsException(登錄失敗次數過多)、IncorrectCredentialsException (錯誤的憑證)、
// ExpiredCredentialsException(過期的憑證)等,具體請查看其繼承關係;
// 對於頁面的錯誤消息展示,最好使用如“用戶名/密碼錯誤”而不是“用戶名錯誤”/“密碼錯誤”,
// 防止一些惡意用戶非法掃描帳號庫;
        try{subject.login(token);}catch (AuthenticationException e){
            System.out.print("登錄失敗");
        }
        Assert.assertEquals(true,subject.isAuthenticated());
            System.out.print("登錄成功");
        //    6、最後可以調用subject.logout退出,其會自動委託給SecurityManager.logout方法退出
        subject.logout();
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章