Shiro系列 | 《Shiro開發詳細教程》第二章:Shiro身份認證

本文目錄:

► 第二章:Shiro身份認證

► 2.1 身份認證

► 2.2 環境準備

► 2.3 登錄、退出

下節文章預告

► 第二章:Shiro身份認證(預告)

► 2.4 身份認證流程(預告)

2.1 身份認證

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

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

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

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

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

最常見的 principals 和 credentials 組合就是用戶名 / 密碼了。

接下來先進行一個基本的身份認證。

2.2 環境準備

環境要求:Maven、IDEA

  • 新建Maven工程,並且導入以下Jar包依賴 (添加 junit、common-logging 及 shiro-core 依賴、log4j、slfj-api等即可):
<dependencies>
    <!--Shiro核心包-->
  <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.0</version>
  </dependency>
    <!--junit測試包-->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>
  <!--日誌管理包-->
  <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
  </dependency>
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.12</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
  </dependency>
</dependencies>

2.3 登錄、退出

  • 準備初始值數據到屬性文件中(shiro.ini)
[users]
zhangsan=mima
sunwukong=mima

說明:使用 ini 配置文件,通過 [users] 指定了兩個主體:zhang/ mima、sunwukong / mima.

  • 添加log4j的日誌輸出格式定義文件log4j.properties
log4j.rootLogger=INFO, 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=TRACE
# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
  • 增加測試類,測試主體信息(com.ms.ShiroTest)
  • 獲取SecurityManager工廠,此處使用Ini配置文件初始化SecurityManager
  • 得到SecurityManager實例 並綁定給SecurityUtils
  • 得到Subject及創建用戶名/密碼身份驗證Token(即用戶身份/憑證)
  • 登錄,即身份驗證
  • 身份驗證成功與否具體信息
  • 退出

具體代碼如下:

/**
 * @Auther: likang
 * @Description: shiro登錄、退出功能演示
 */
public class ShiroTest {

    public static final Logger logger = LoggerFactory.getLogger(ShiroTest.class);

    public static void main(String[] args) {
        //1:獲取SecurityManager工廠,此處使用Ini配置文件初始化SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //2:得到SecurityManager實例 並綁定給SecurityUtils
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        //3:得到Subject及創建用戶名/密碼身份驗證Token(即用戶身份/憑證)
        Subject subject = SecurityUtils.getSubject();
        //4:登錄,即身份驗證
        if (!subject.isAuthenticated()){
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "mima2");
           try {
                subject.login(token);
                logger.info("登錄成功");
            } catch (UnknownAccountException e) {
            //5:身份驗證失敗
                logger.info("用戶名錯誤或者不存在");
            } catch (IncorrectCredentialsException e){
                logger.info("密碼不匹配");
            } catch (LockedAccountException e){
                logger.info("用戶已被鎖定,請聯繫管理員");
            } catch (DisabledAccountException e){
                logger.info("用戶已被禁用,請聯繫管理員");
            } catch (ExcessiveAttemptsException e){
                logger.info("用戶登錄次數過多");
            } catch (AuthenticationException  e){
                logger.info("用戶登錄失敗,請聯繫管理員");
            }
        }
        subject.logout();//6:退出
    }
}

代碼梳理:

  • 首先通過 new IniSecurityManagerFactory 並指定一個 ini 配置文件來創建一個 SecurityManager 工廠;
  • 接着獲取 SecurityManager 並綁定到 SecurityUtils,這是一個全局設置,設置一次即可;
  • 通過 SecurityUtils 得到 Subject,其會自動綁定到當前線程;如果在 web 環境在請求結束時需要解除綁定;然後獲取身份驗證的 Token,如用戶名 / 密碼;
  • 調用 subject.login 方法進行登錄,其會自動委託給 SecurityManager.login 方法進行登錄;
  • 如果身份驗證失敗請捕獲 AuthenticationException 或其子類,常見的如:

DisabledAccountException(禁用的帳號)、

LockedAccountException(鎖定的帳號)、

UnknownAccountException(錯誤的帳號)、

ExcessiveAttemptsException(登錄失敗次數過多)、

IncorrectCredentialsException (錯誤的憑證)、

ExpiredCredentialsException(過期的憑證)等,

對於頁面的錯誤消息展示,最好使用如 “用戶名 / 密碼錯誤” 而不是 “用戶名錯誤”/“密碼錯誤”,防止一些惡意用戶非法掃描帳號庫;

  • 最後可以調用 subject.logout 退出,其會自動委託給 SecurityManager.logout 方法退出。

從如上代碼可總結出身份驗證的步驟

  • 收集用戶身份 / 憑證,即如用戶名 / 密碼;
  • 調用 Subject.login 進行登錄,如果失敗將得到相應的 AuthenticationException 異常,根據異常提示用戶錯誤信息;否則登錄成功;
  • 最後調用 Subject.logout 進行退出操作。

需要解決的問題?

  • 用戶名 / 密碼硬編碼在 ini 配置文件,以後需要改成如數據庫存儲,且密碼需要加密存儲;
  • 用戶身份 Token 可能不僅僅是用戶名 / 密碼,也可能還有其他的,如登錄時允許用戶名 / 郵箱 / 手機號同時登錄。

可以留言歡迎討論,如何解決這些問題,答案下期揭曉。

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