寫在前面:
小伙伴兒們,大家好!這次讓我們一起來學習Shiro權限框架吧!
思維導圖:
1,初識Shiro
1.1,簡介;
- Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。使用Shiro的易於理解的API,您可以快速、輕松地獲得任何應用程序,從最小的移動應用程序到最大的網絡和企業應用程序。
1.2,入門配置;
創建Maven工程命名為Shiro,結構圖如下;
直接引入依賴:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
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
shiro.ini文件(放在resources目錄下),以鍵值對地形式存放:
[users]
java=123456
jack=123
1.3,程序文件;
我們新建一個HelloWorld類:
package com.java; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; public class HelloWorld { public static void main(String[] args) { // 讀取配置文件,初始化SecurityManager工廠 Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini"); // 獲取securityManager實例 SecurityManager securityManager=factory.getInstance(); // 把securityManager實例綁定到SecurityUtils SecurityUtils.setSecurityManager(securityManager); // 得到當前執行的用戶 Subject currentUser=SecurityUtils.getSubject(); // 創建token令牌,用戶名/密碼 UsernamePasswordToken token=new UsernamePasswordToken("java", "123456"); try{ // 身份認證 currentUser.login(token); System.out.println("身份認證成功!"); }catch(AuthenticationException e){ e.printStackTrace(); System.out.println("身份認證失敗!"); } // 退出 currentUser.logout(); } }
- 首先通過 new IniSecurityManagerFa ctory 並指定一個 ini 配置文件來創建一個 SecurityManager 工廠;
- 接着獲取 SecurityManager 並綁定到 SecurityUtils,這是一個全局設置,設置一次即可;
- 通過 SecurityUtils 得到 Subject,其會自動綁定到當前線程;如果在 web 環境在請求結束時需要解除綁定;然后獲取身份驗證的 Token,如用戶名 / 密碼;
- 調用 subject.login 方法進行登錄,其會自動委托給 SecurityManager.login 方法進行登錄;
- 如果身份驗證失敗請捕獲 Authenticat ionException 或其子類,常見的如: DisabledAccountException(禁用的帳號)、LockedAccountException(鎖定的帳號)、UnknownAccountExceptio n(錯誤的帳號)、ExcessiveAttempts Exception(登錄失敗次數過多)、In correctCredentialsException (錯誤的憑證)、ExpiredCredentialsException (過期的憑證)等,具體請查看其繼承關系;對於頁面的錯誤消息展示,最好使用如 “用戶名 / 密碼錯誤” 而不是 “用戶名錯誤”/“密碼錯誤”,防止一些惡意用戶非法掃描帳號庫;
- 最后可以調用 subject.logout 退出,其會自動委托給 SecurityManager.logout 方法退出。
運行:
從如上代碼可總結出身份驗證的步驟:
- 收集用戶身份 / 憑證,即如用戶名 / 密碼;
- 調用 Subject.login 進行登錄,如果失敗將得到相應的 AuthenticationException 異常,根據異常提示用戶錯誤信息;否則登錄成功;
- 最后調用 Subject.logout 進行退出操作。
2,Shiro身份認證;
2.1,Subject認證主體;
在 shiro 中,用戶需要提供 principals (身份)和 credentials(證明)給 shiro,從而應用能驗證用戶身份:
principals:身份,即主體的標識屬性,可以是任何東西,如用戶名、郵箱等,唯一即可。一個主體可以有多個 principals ,但只有一個 Primary principals,一般是用戶名 / 密碼 / 手機號。
credentials:證明 / 憑證,即只有主體知道的安全值,如密碼 / 數字證書等。 最常見的 principals 和 credentials 組合就是用戶名 / 密碼了。接下來先進行一個基本的身份認證。 另外兩個相關的概念是之前提到的 Subject 及 Realm,分別是主體及驗證主體的數據源。
2.2,身份認證流程;
流程如下:
- 首先調用 Subject.login(token) 進行登錄,其會自動委托給 Security Manager ,調用之前必須通過 SecurityUtils.set SecurityManager() 設置;
- SecurityManager 負責真正的身份驗證邏輯;它會委托給 Authenticator 進行身份驗證;
- Authenticator 才是真正的身份驗證者,Shiro API 中核心的身份認證入口點,此處可以自定義插入自己的實現;
- Authenticator 可能會委托給相應的 AuthenticationStrategy 進行多 Realm 身份驗證,默認 ModularRealmAuthen ticator 會調用 AuthenticationStrategy 進行多 Realm 身份驗證;
- Authenticator 會把相應的 token 傳入 Realm,從 Realm 獲取身份驗證信息,如果沒有返回 / 拋出異常表示身份驗證失敗了。此處可以配置多個 Realm,將按照相應的順序及策略進行訪問。
2.3,Realm;
Realm:域,Shiro 從從 Realm 獲取安全數據(如用戶、角色、權限),就是說 SecurityManager 要驗證用戶身份,那么它需要從 Realm 獲取相應的用戶進行比較以確定用戶身份是否合法;也需要從 Realm 得到用戶相應的角色 / 權限進行驗證用戶是否能進行操作;可以把 Realm 看成 DataSource,即安全數據源。如我們之前的 ini 配置方式將使用 org.apache.shiro .realm.text.IniRealm。
realm種類很多,例如常見的 jdbc realm,jndi realm,text realm,我們這里就了解一下jdbc realm,其他的不做敘述;
-
首先先建立數據庫db_shiro,然后添加表users(表名稱不可改),添加一條數據;
-
配置文件jdbc_realm.ini,這與上文的shiro.ini是一樣的;
[main]
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/db_shiro
dataSource.username=root
dataSource.password=123456
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm -
導入依賴
<!-- mysql 數據庫及 druid 連接池-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>0.2.23</version>
</dependency> -
測試程序(把路徑更改一下即可)
package com.java; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; public class JdbcRealmTest { public static void main(String[] args) { // 讀取配置文件,初始化SecurityManager工廠 Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:jdbc_realm.ini"); // 獲取securityManager實例 SecurityManager securityManager=factory.getInstance(); // 把securityManager實例綁定到SecurityUtils SecurityUtils.setSecurityManager(securityManager); // 得到當前執行的用戶 Subject currentUser=SecurityUtils.getSubject(); // 創建token令牌,用戶名/密碼 UsernamePasswordToken token=new UsernamePasswordToken("java", "123"); try{ // 身份認證 currentUser.login(token); System.out.println("身份認證成功!"); }catch(AuthenticationException e){ e.printStackTrace(); System.out.println("身份認證失敗!"); } // 退出 currentUser.logout(); } }
-
運行結果
好了,今天就先分享到這里了,下期繼續給大家帶來Shiro相關方面的學習! 更多干貨、優質文章,歡迎關注我的原創技術公眾號~