shiro身份驗證
身份驗證,即在應用中誰能證明他就是他本人。一般提供如他們的身份ID一些標識信息來表明他就是他本人,如提供身份證,用戶名/密碼來證明。
在shiro中,用戶需要提供principals (身份)和credentials(證明)給shiro,從而應用能驗證用戶身份:
principals:身份,即主體的標識屬性,可以是任何東西,如用戶名、郵箱等,唯一即可。一個主體可以有多個principals,但只有一個Primary principals,一般是用戶名/密碼/手機號。
credentials:證明/憑證,即只有主體知道的安全值,如密碼/數字證書等。
最常見的principals和credentials組合就是用戶名/密碼了。
例如在AuthenticationToken 就只提供了兩個方法,分別獲取身份和憑證。AuthenticationToken 的使用的實現一般就是UsernamePasswordToken,但是可以自定義實現,例如JWT token。
public interface AuthenticationToken extends Serializable { Object getPrincipal(); Object getCredentials(); }
對於身份驗證的流程我們同樣可以從外部和內部兩個角度來看
一、外部實現流程
1.、首先準備一些用戶身份/憑據(shiro.ini)
- [users]
- zhang=123
- wang=123
此處使用ini配置文件,通過[users]指定了兩個主體:zhang/123、wang/123。
2、測試用例
@Test public void testHelloworld() { //1、獲取SecurityManager工廠,此處使用Ini配置文件初始化SecurityManager Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //2、得到SecurityManager實例 並綁定給SecurityUtils org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); //3、得到Subject及創建用戶名/密碼身份驗證Token(即用戶身份/憑證) Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123"); try { //4、登錄,即身份驗證 subject.login(token); } catch (AuthenticationException e) { //5、身份驗證失敗 } Assert.assertEquals(true, subject.isAuthenticated()); //斷言用戶已經登錄 //6、退出 subject.logout(); }
2.1、首先通過new IniSecurityManagerFactory並指定一個ini配置文件來創建一個SecurityManager工廠;
2.2、接著獲取SecurityManager並綁定到SecurityUtils,這是一個全局設置,設置一次即可;
2.3、通過SecurityUtils得到Subject,其會自動綁定到當前線程;如果在web環境在請求結束時需要解除綁定;然後獲取身份驗證的Token,如用戶名/密碼;
2.4、調用subject.login方法進行登錄,其會自動委托給SecurityManager.login方法進行登錄;
2.5、如果身份驗證失敗請捕獲AuthenticationException或其子類,常見的如: DisabledAccountException(禁用的帳號)、LockedAccountException(鎖定的帳號)、UnknownAccountException(錯誤的帳號)、ExcessiveAttemptsException(登錄失敗次數過多)、IncorrectCredentialsException (錯誤的憑證)、ExpiredCredentialsException(過期的憑證)等,具體請查看其繼承關系;對於頁面的錯誤消息展示,最好使用如“用戶名/密碼錯誤”而不是“用戶名錯誤”/“密碼錯誤”,防止一些惡意用戶非法掃描帳號庫;
2.6、最後可以調用subject.logout退出,其會自動委托給SecurityManager.logout方法退出。
3.Realm實現
org.apache.shiro.realm.Realm接口如下:
String getName(); //返回一個唯一的Realm名字 boolean supports(AuthenticationToken token); //判斷此Realm是否支持此Token AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException; //根據Token獲取認證信息
自定義Realm實現:
public class MyRealm1 implements Realm { @Override public String getName() { return "myrealm1"; } @Override public boolean supports(AuthenticationToken token) { //僅支持UsernamePasswordToken類型的Token return token instanceof UsernamePasswordToken; } @Override public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String)token.getPrincipal(); //得到用戶名 String password = new String((char[])token.getCredentials()); //得到密碼 if(!"zhang".equals(username)) { throw new UnknownAccountException(); //如果用戶名錯誤 } if(!"123".equals(password)) { throw new IncorrectCredentialsException(); //如果密碼錯誤 } //如果身份認證驗證成功,返回一個AuthenticationInfo實現; return new SimpleAuthenticationInfo(username, password, getName()); } }
二、從內部實現原理來看
流程如下:
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,將按照相應的順序及策略進行訪問。
摘自:第二章 身份驗證——《跟我學Shiro》
shiro身份驗證