認證與授權 --- Shiro(一)

認證與授權 — Shiro(一)

認證與授權一般主流的框架就是 ShiroSpringSecurity, Shiro 相對來說較於簡單, 而且我們注意到一個神奇的東東, Spring 竟然也在用 Shiro

file

真是有圖有真相啊…

file

而且帥帥最近和朋友一起開發的項目也使用了 Shiro, 因此做一些 Shiro 的分享.

1. Shiro 簡介

  • Apache Shiro 是 Java 的一個安全(權限)框架
  • Shiro 可以非常容易的開發出足夠好的應用,其不僅可以用在 JavaSE 環境,也可以用在 JavaEE 環境
  • Shiro 可以完成:認證、授權、加密、會話管理、與Web 集成、緩存 等

2. 功能簡介

Apache Shiro是具有許多功能的全面的應用程序安全框架。下圖顯示了Shiro的核心功能:

file

Shiro以Shiro開發團隊所謂的“應用程序安全性的四個基石”爲目標-身份驗證,授權,會話管理和加密:

  • Authentication:身份認證/登錄,驗證用戶是不是擁有相應的身份;
  • Authorization:授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用 戶是否能進行什麼操作,如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶 對某個資源是否具有某個權限;
  • Session Manager:會話管理,即用戶登錄後就是一次會話,在沒有退出之前,它的所有 信息都在會話中;會話可以是普通 JavaSE 環境,也可以是 Web 環境的;
  • Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;

在不同的應用程序環境中,還具有其他功能來支持和加強這些問題,主要有:

  • Web Support:Web 支持,可以非常容易的集成到Web 環境;
  • Caching:緩存,比如用戶登錄後,其用戶信息、擁有的角色/權限不必每次去查,這樣可 以提高效率;
  • Concurrency:Shiro 支持多線程應用的併發驗證,即如在一個線程中開啓另一個線程,能
  • 把權限自動傳播過去;
  • Testing:提供測試支持;
  • Run As:允許一個用戶假裝爲另一個用戶(如果他們允許)的身份進行訪問;
  • Remember Me:記住我,這個是非常常見的功能,即一次登錄後,下次再來的話不用登 錄了

3. Shiro 架構

3.1 從 Shiro 外部來看

file

  • Subject:應用代碼直接交互的對象是 Subject,也就是說 Shiro 的對外 API 核心就是 Subject。Subject 代表了當前“用戶”, 這個用戶不一定 是一個具體的人,與當前應用交互的任何東西都是 Subject,如網絡爬蟲, 機器人等;與 Subject 的所有交互都會委託給 SecurityManager; Subject 其實是一個門面,SecurityManager 纔是實際的執行者;
  • SecurityManager:安全管理器;即所有與安全有關的操作都會與 SecurityManager 交互;且其管理着所有 Subject;可以看出它是 Shiro 的核心,它負責與 Shiro 的其他組件進行交互,它相當於 SpringMVC 中 DispatcherServlet 的角色
  • Realm:Shiro 從 Realm 獲取安全數據(如用戶、角色、權限),就是說 SecurityManager 要驗證用戶身份,那麼它需要從 Realm 獲取相應的用戶進行比較以確定用戶身份是否合法;也需要從 Realm 得到用戶相應的角色/ 權限進行驗證用戶是否能進行操作;可以把 Realm 看成 DataSource

3.2 從 Shiro 內部來看

file

  • Subject: 任何可以與應用交互的“用戶”;
  • SecurityManager : 相當於SpringMVC 中的 DispatcherServlet;是 Shiro 的核心; 所有具體的交互都通過 SecurityManager 進行控制;它管理着所有 Subject、且負責進 行認證、授權、會話及緩存的管理。
  • Authenticator: 負責 Subject 認證,是一個擴展點,可以自定義實現;可以使用認證策略(Authentication Strategy),即什麼情況下算用戶認證通過了;
  • Authorizer: 授權器、即訪問控制器,用來決定主體是否有權限進行相應的操作;即控 制着用戶能訪問應用中的哪些功能;
  • Realm:可以有 1 個或多個 Realm,可以認爲是安全實體數據源,即用於獲取安全實體的;可以是JDBC 實現,也可以是內存實現等等;由用戶提供;所以一般在應用中都需要實現自己的 Realm;
  • SessionManager: 管理 Session 生命週期的組件;而 Shiro 並不僅僅可以用在 Web 環境,也可以用在如普通的 JavaSE 環境
  • CacheManager: 緩存控制器,來管理如用戶、角色、權限等的緩存的;因爲這些數據基本上很少改變,放到緩存中後可以提高訪問的性能
  • Cryptography: 密碼模塊,Shiro 提高了一些常見的加密組件用於如密碼加密/解密。

4. Quickstart

Shiro 爲我們提供了一些入門學習的實例代碼, 我們可以下載 https://downloads.apache.org/shiro/1.5.3/shiro-root-1.5.3-source-release.zip 學習.

4.1 創建一個 Maven 項目

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>club.javafamily</groupId>
   <artifactId>shiro01</artifactId>
   <version>1.0-SNAPSHOT</version>

   <properties>
      <log4j.version>1.2.17</log4j.version>
      <slf4j.version>1.7.26</slf4j.version>
   </properties>

   <dependencies>
      <dependency>
         <groupId>org.apache.shiro</groupId>
         <artifactId>shiro-core</artifactId>
         <version>1.4.2</version>
      </dependency>
      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>jcl-over-slf4j</artifactId>
         <scope>runtime</scope>
         <version>${slf4j.version}</version>
      </dependency>
      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-log4j12</artifactId>
         <scope>runtime</scope>
         <version>${slf4j.version}</version>
      </dependency>
      <dependency>
         <groupId>log4j</groupId>
         <artifactId>log4j</artifactId>
         <scope>runtime</scope>
         <version>${log4j.version}</version>
      </dependency>

   </dependencies>

</project>

4.2 Log4j.properties

# 注意我這裏把 Log 級別提到了 Error
log4j.rootLogger=ERROR, 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

# General Apache libraries
log4j.logger.org.apache=WARN

# Spring
log4j.logger.org.springframework=WARN

# Default Shiro logging
log4j.logger.org.apache.shiro=INFO

# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

4.3 shiro.ini

[users]
# 用戶 jack, 密碼爲 JavaFamily, 擁有 dev 和 test 兩個 role
jack = JavaFamily, dev, test

[roles]
# admin role 擁有所有權限
admin = *
# dev role
dev = code:commit, code:checkout, code:test:write, code:test:read
# test role
test = code:checkout, code:test:read

** 使用 * 表示 role 擁有所有權限 **

4.4 創建程序入口類 Quickstart.java 並檢查代碼結構

file

4.5 代碼分析

package club.javafamily.shiro01;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

public class Quickstart {

    public static void main(String[] args) {

        // 通過 ini 文件創建一個 SecurityManager 的工廠(生產環境不會這麼用)
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        // 獲取 SecurityManager
        SecurityManager securityManager = factory.getInstance();

        // 設置 SecurityManager 爲單例, 以後通過 SecurityUtils(與 Spring 整合後沒有意義)
        SecurityUtils.setSecurityManager(securityManager);

        // 獲取 Subject 對象
        Subject currentUser = SecurityUtils.getSubject();

        // 測試使用 Session 對象 ---> 注意這是 SE 項目, 不是 web 項目, 依然可以用 Session
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");

        System.out.println("===測試 Session 對象: someKey = " + value);

        // 測試登錄
        if (!currentUser.isAuthenticated()) { // 如果用戶沒有登錄
            // 通過用戶名和密碼構造 UsernamePasswordToken 用於驗證
            // 這裏的的 jack---JavaFamily 用戶名和密碼來自於 shiro.ini 文件
            UsernamePasswordToken token = new UsernamePasswordToken("jack", "JavaFamily");
            // 設置 RememberMe
            token.setRememberMe(true);
            try {
                // 執行登錄
                currentUser.login(token);
            } catch (UnknownAccountException uae) {
                System.out.println("賬戶不存在" + token.getPrincipal());
                return;
            } catch (IncorrectCredentialsException ice) {
                System.out.println("密碼不正確: " + token.getPrincipal());
                return;
            } catch (LockedAccountException lae) {
                System.out.println("賬戶已經登錄: " + token.getPrincipal());
                return;
            }
            catch (AuthenticationException ae) {
                System.out.println("登錄失敗! 請過會再試!");
                return;
            }
        }

        System.out.println("用戶 " + currentUser.getPrincipal() + " 已成功登錄!");

        // 測試 Role
        if (currentUser.hasRole("dev")) {
            System.out.println("用戶" + currentUser.getPrincipal() + " 屬於 dev Role");
        } else {
            System.out.println("用戶" + currentUser.getPrincipal() + " 不是 dev Role");
        }

        // 測試權限
        if (currentUser.isPermitted("code:commit")) {
            System.out.println("用戶 " + currentUser.getPrincipal() + " 擁有代碼提交的權限.");
        } else {
            System.out.println("用戶 " + currentUser.getPrincipal() + " 沒有代碼提交的權限.");
        }

        if (currentUser.isPermitted("code:test:write")) {
            System.out.println("用戶 " + currentUser.getPrincipal() + " 擁有 code:test:write 權限");
        } else {
            System.out.println("用戶 " + currentUser.getPrincipal() + " 沒有 code:test:write 權限");
        }

        if (currentUser.isPermitted("code:test:delete")) {
            System.out.println("用戶 " + currentUser.getPrincipal() + " 擁有 code:test:delete 權限");
        } else {
            System.out.println("用戶 " + currentUser.getPrincipal() + " 沒有 code:test:delete 權限");
        }


        //註銷
        currentUser.logout();

        System.exit(0);
    }
}

** 分析帥帥已經用註釋給大家標註了, 就不多囉嗦了. **

4.6 運行結果

file

** 最近幾期文章可能都是關於 Shiro 的哦, 大家不要着急, 因爲最近和朋友開發一個項目要用, 所以臨時做一些 Shiro 的學習分享, 後面也有 Spring Security 系列的, 感興趣的持續關注哦! **

file

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