天天看點

Shiro安全架構快速入門1、關于Shiro2、快速入門

目錄

1、關于Shiro

官網介紹

Shiro特點

Shiro基本功能點

Shiro架構

2、快速入門

2.1、Shiro身份驗證

2.2、Shiro授權

1、關于Shiro

官網介紹

Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.

Apache Shiro™是一個強大且易用的 Java 安全架構, 能夠用于身份驗證、授權、加密和會話管理。Shiro 擁有易于了解的 API, 您可以快速、輕松地獲得任何應用程式——從最小的移動應用程式到最大的網絡和企業應用程式。

簡而言之,Apache Shiro 是一個強大靈活的開源安全架構,可以完全處理身份驗證、授權、加密和會話管理。

Shiro特點

  • 易于使用——易用性是項目的最終目标。應用程式安全非常令人困惑和沮喪, 被認為是 “不可避免的災難”。如果你讓它簡化到新手都可以使用它, 它就将不再是一種痛苦了。
  • 全面——沒有其他安全架構的寬度範圍可以同 Apache Shiro 一樣, 它可以成為你的 “一站式” 為您的安全需求提供保障。
  • 靈活——Apache Shiro 可以在任何應用程式環境中工作。雖然在網絡工作、EJB 和 IoC 環境中可能并不需要它。但 Shiro 的授權也沒有任何規範, 甚至沒有許多依賴關系。
  • Web 支援——Apache Shiro 擁有令人興奮的 web 應用程式支援, 允許您基于應用程式的 url 建立靈活的安全政策和網絡協定 (例如 REST), 同時還提供一組 JSP 庫控制頁面輸出。
  • 低耦合——Shiro 幹淨的 API 和設計模式使它容易與許多其他架構和應用程式內建。你會看到 Shiro 無縫地內建 Spring 這樣的架構, 以及 Grails, Wicket, Tapestry, Mule, Apache Camel, Vaadin... 等。
  • 被廣泛支援——Apache Shiro 是 Apache 軟體基金會的一部分。項目開發和使用者組都有友好的網民願意幫助。這樣的商業公司如果需要 Katasoft 還提供專業的支援和服務。

Shiro基本功能點

  • Authentication:身份認證 / 登入,驗證使用者是不是擁有相應的身份;
  • Authorization:授權,即權限驗證,驗證某個已認證的使用者是否擁有某個權限;即判斷使用者是否能做事情,常見的如:驗證某個使用者是否擁有某個角色。或者細粒度的驗證某個使用者對某個資源是否具有某個權限;
  • Session Management:會話管理,即使用者登入後就是一次會話,在沒有退出之前,它的所有資訊都在會話中;會話可以是普通 JavaSE 環境的,也可以是如 Web 環境的;
  • Cryptography:加密,保護資料的安全性,如密碼加密存儲到資料庫,而不是明文存儲;
  • Web Support:Web 支援,可以非常容易的內建到 Web 環境;
  • Caching:緩存,比如使用者登入後,其使用者資訊、擁有的角色 / 權限不必每次去查,這樣可以提高效率;
  • Concurrency:shiro 支援多線程應用的并發驗證,即如在一個線程中開啟另一個線程,能把權限自動傳播過去;
  • Testing:提供測試支援;
  • Run As:允許一個使用者假裝為另一個使用者(如果他們允許)的身份進行通路;
  • Remember Me:記住我,這個是非常常見的功能,即一次登入後,下次再來的話不用登入了。

    記住一點,Shiro 不會去維護使用者、維護權限;這些需要我們自己去設計 / 提供;然後通過相應的接口注入給 Shiro 即可。

Shiro架構

在概念層,Shiro 架構包含三個主要的理念:Subject,SecurityManager 和 Realm。下面的圖展示了這些元件如何互相作用,我們将在下面依次對其進行描述。

Shiro安全架構快速入門1、關于Shiro2、快速入門
  • Subject:目前使用者,Subject 可以是一個人,但也可以是第三方服務、守護程序帳戶、時鐘守護任務或者其它–目前和軟體互動的任何事件。
  • SecurityManager:管理所有 Subject,SecurityManager 是 Shiro 架構的核心,配合内部安全元件共同組成安全傘。
  • Realms:用于進行權限資訊的驗證,我們自己實作。Realm 本質上是一個特定的安全 DAO:它封裝與資料源連接配接的細節,得到 Shiro 所需的相關的資料。在配置 Shiro 的時候,你必須指定至少一個 Realm 來實作認證(authentication)和 / 或授權(authorization)。

我們需要實作 Realms 的 Authentication 和 Authorization。其中 Authentication 是用來驗證使用者身份,Authorization 是授權通路控制,用于對使用者進行的操作授權,證明該使用者是否允許進行目前操作,如通路某個連結,某個資源檔案等。

2、快速入門

2.1、Shiro身份驗證

Shiro安全架構快速入門1、關于Shiro2、快速入門

                    shiro認證流程圖

示例代碼:

1、建立maven工程,引入相關依賴

<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-core</artifactId>
   <version>1.4.0</version>
</dependency>
<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.12</version>
</dependency>
           

2、建立測試類

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

public class AuthenticationTest {

    SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    // 在方法開始前添加一個使用者
    @Before
    public void addUser() {
        simpleAccountRealm.addAccount("admin", "111111");
    }

    @Test
    public void testAuthentication() {
        // 1.建構SecurityManager環境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(simpleAccountRealm);

        // 2.主體送出認證請求
        SecurityUtils.setSecurityManager(defaultSecurityManager); // 設定SecurityManager環境
        Subject subject = SecurityUtils.getSubject(); // 擷取目前主體

        UsernamePasswordToken token = new UsernamePasswordToken("admin", "111111");
        subject.login(token); // 登入

        // subject.isAuthenticated()方法傳回一個boolean值,用于判斷使用者是否認證成功
        System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 輸出true

        subject.logout(); // 登出

        System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 輸出false
    }
}
           

3、運作并檢視結果

     運作之後可以看到isAuthenticated先列印true,說明認證成功;第二次列印為false,是因為使用者已登出,認證失敗。

具體的身份驗證流程:

Shiro安全架構快速入門1、關于Shiro2、快速入門

流程如下:

  1. 首先調用 

    Subject.login(token)

     進行登入,其會自動委托給 

    Security Manager

    ,調用之前必須通過 

    SecurityUtils.setSecurityManager()

     設定;
  2. SecurityManager

     負責真正的身份驗證邏輯;它會委托給 

    Authenticator

     進行身份驗證;
  3. Authenticator

     才是真正的身份驗證者,

    Shiro API

     中核心的身份認證入口點,此處可以自定義插入自己的實作;
  4. Authenticator

     可能會委托給相應的 

    AuthenticationStrategy

     進行多 

    Realm

     身份驗證,預設 

    ModularRealmAuthenticator

     會調用 

    AuthenticationStrategy

     進行多 

    Realm

     身份驗證;
  5. Authenticator 

    會把相應的 

    token

     傳入 

    Realm

    ,從 

    Realm

     擷取身份驗證資訊,如果沒有傳回 / 抛出異常表示身份驗證失敗了。此處可以配置多個 

    Realm

    ,将按照相應的順序及政策進行通路。

2.2、Shiro授權

      授權,也叫通路控制,即在應用中控制誰能通路哪些資源(如通路頁面/編輯資料/頁面操作等)。在授權中需了解的幾個關鍵對象:主體(Subject)、資源(Resource)、權限(Permission)、角色(Role)。

主體

主體,即通路應用的使用者,在 Shiro 中使用 Subject 代表該使用者。使用者隻有授權後才允許通路相應的資源。

資源

在應用中使用者可以通路的URL,比如通路 JSP 頁面、檢視/編輯某些資料、通路某個業務方法、列印文本等等都是資源。使用者隻要授權後才能通路。

權限

安全政策中的原子授權機關,通過權限我們可以表示在應用中使用者有沒有操作某個資源的權力。即權限表示在應用中使用者能不能通路某個資源,如: 通路使用者清單頁面

檢視/新增/修改/删除使用者資料(即很多時候都是 CRUD(增查改删)式權限控制)列印文檔等。如上可以看出,權限代表了使用者有沒有操作某個資源的權利,即反映在某個資源上的操作允不允許,不反映誰去執行這個操作。是以後續還需要把權限賦予給使用者,即定義哪個使用者允許在某個資源上做什麼操作(權限),Shiro 不會去做這件事情,而是由實作人員提供。

角色

角色代表了操作集合,可以了解為權限的集合,一般情況下我們會賦予使用者角色而不是權限,即這樣使用者可以擁有一組權限,賦予權限時比較友善。典型的如:項目經理、技術總監、CTO、開發工程師等都是角色,不同的角色擁有一組不同的權限。

隐式角色:

即直接通過角色來驗證使用者有沒有操作權限,如在應用中 CTO、技術總監、開發工程師可以使用列印機,假設某天不允許開發工程師使用列印機,此時需要從應用中删除相應代碼;再如在應用中 CTO、技術總監可以檢視使用者、檢視權限;突然有一天不允許技術總監檢視使用者、檢視權限了,需要在相關代碼中把技術總監角色從判斷邏輯中删除掉;即粒度是以角色為機關進行通路控制的,粒度較粗;如果進行修改可能造成多處代碼修改。

顯式角色:

在程式中通過權限控制誰能通路某個資源,角色聚合一組權限集合;這樣假設哪個角色不能通路某個資源,隻需要從角色代表的權限集合中移除即可;無須修改多處代碼;即粒度是以資源/執行個體為機關的;粒度較細。

Shiro安全架構快速入門1、關于Shiro2、快速入門

                           shiro授權流程圖

示例代碼:

1、引入相關依賴,參考2.1節

2、建立測試類

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

public class AuthorizationTest {
    SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before // 在方法開始前添加一個使用者,讓它具備admin和user兩個角色
    public void addUser() {
        simpleAccountRealm.addAccount("test", "111111", "admin", "user");
    }

    @Test
    public void testAuthorization() {

        // 1.建構SecurityManager環境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(simpleAccountRealm);

        // 2.主體送出認證請求
        SecurityUtils.setSecurityManager(defaultSecurityManager); // 設定SecurityManager環境
        Subject subject = SecurityUtils.getSubject(); // 擷取目前主體

        UsernamePasswordToken token = new UsernamePasswordToken("test", "111111");
        subject.login(token); // 登入

        // subject.isAuthenticated()方法傳回一個boolean值,用于判斷使用者是否認證成功
        System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 輸出true
        // 判斷subject是否具有admin和user兩個角色權限,如沒有則會報錯
        subject.checkRoles("admin","user");
        System.out.println("使用者具有admin權限: "+subject.hasRole("admin"));
        System.out.println("使用者具有guest權限: "+subject.hasRole("guest"));
    }
}
           

3、運作并檢視結果

Shiro安全架構快速入門1、關于Shiro2、快速入門

具體的授權流程:

Shiro安全架構快速入門1、關于Shiro2、快速入門

流程如下:

  1. 首先調用 

    Subject.isPermitted*/hasRole*

    接口,其會委托給 SecurityManager,而 SecurityManager 接着會委托給 Authorizer;
  2. Authorizer 是真正的授權者,如果我們調用如 isPermitted(“user:view”),其首先會通過 PermissionResolver 把字元串轉換成相應的 Permission 執行個體;
  3. 在進行授權之前,其會調用相應的 Realm 擷取 Subject 相應的角色/權限用于比對傳入的角色/權限;
  4. Authorizer 會判斷 Realm 的角色/權限是否和傳入的比對,如果有多個 Realm,會委托給 ModularRealmAuthorizer 進行循環判斷,如果比對如 

    isPermitted*/hasRole*

     會傳回 true,否則傳回 false 表示授權失敗。

ModularRealmAuthorizer 進行多 Realm 比對流程:

  • 首先檢查相應的 Realm 是否實作了實作了 Authorizer;
  • 如果實作了 Authorizer,那麼接着調用其相應的 

    isPermitted*/hasRole*

     接口進行比對;
  • 如果有一個 Realm 比對那麼将傳回 true,否則傳回 false。

如果 Realm 進行授權的話,應該繼承 AuthorizingRealm,其流程是:

  • 如果調用 

    hasRole*

    ,則直接擷取 AuthorizationInfo.getRoles() 與傳入的角色比較即可;首先如果調用如 isPermitted(“user:view”),首先通過 PermissionResolver 将權限字元串轉換成相應的 Permission 執行個體,預設使用 WildcardPermissionResolver,即轉換為通配符的 WildcardPermission;
  • 通過 AuthorizationInfo.getObjectPermissions() 得到 Permission 執行個體集合;通過 AuthorizationInfo.getStringPermissions() 得到字元串集合并通過 PermissionResolver 解析為 Permission 執行個體;然後擷取使用者的角色,并通過 RolePermissionResolver 解析角色對應的權限集合(預設沒有實作,可以自己提供);
  • 接着調用 Permission.implies(Permission p) 逐個與傳入的權限比較,如果有比對的則傳回 true,否則 false。