您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)什么是Shiro驗(yàn)證,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
shiro驗(yàn)證:用戶需要提供系統(tǒng)理解和信任的某種身份證明(密碼、證書等),來(lái)證明自己可以登錄該系統(tǒng)。
在驗(yàn)證階段需要先理解幾個(gè)術(shù)語(yǔ):
Subject(主體)- 可以通俗的理解為訪問(wèn)應(yīng)用程序的用戶。只不過(guò)這個(gè)用戶可以是人、第三方進(jìn)程等等。
Principals(身份)- 主體的唯一標(biāo)識(shí)屬性。例如:身份證號(hào)碼、手機(jī)號(hào)、郵箱等等。
Credentials(憑證)- 只有主體知道的安全值。例如:密碼、數(shù)字證書等等。
Realms(領(lǐng)域)- 數(shù)據(jù)訪問(wèn)對(duì)象。用來(lái)獲取Principals和Credentials。例如:用戶名和密碼存放在.ini文件中,可以通過(guò)IniRealm獲取用戶名和密碼、用戶名和密碼存放在數(shù)據(jù)庫(kù)中,可以通過(guò)JdbcRealm獲取用戶名和密碼等等。
AuthenticationToken(驗(yàn)證令牌)- 結(jié)合Principals(身份)和Credentials(憑證)可獲得的用戶身份令牌。
Authenticator (驗(yàn)證者) - 驗(yàn)證用戶驗(yàn)證令牌是否滿足要求的科目。
AuthenticationStrategy(身份驗(yàn)證策略)- 表示以何種策略驗(yàn)證用戶身份。
如下是從官網(wǎng)獲取的驗(yàn)證架構(gòu)圖:
步驟1:收集主題提交的 Principals 和 Credentials 得到 Token,應(yīng)用程序代碼調(diào)用Subject.login方法,傳入 Token 以進(jìn)行身份驗(yàn)證動(dòng)作。
步驟2:Subject實(shí)例,通常是 DelegatingSubject(或子類)通過(guò)調(diào)用 securityManager.login(token)將實(shí)際的身份驗(yàn)證工作委托給 SecurityManager 處理
步驟3:SecurityManager 作為基本的組件,接收 Token 然后通過(guò)調(diào)用 authenticate(token)將其委托給 Authenticator。Authenticator 幾乎總是實(shí)例 ModularRealmAuthenticator,它支持在身份驗(yàn)證期間協(xié)調(diào)一個(gè)或多個(gè) Realm 實(shí)例。
步驟4:如果為應(yīng)用配置了多個(gè) Realm ,則 ModularRealmAuthenticator 實(shí)例將使用其配置的 AuthenticationStrategy 啟動(dòng)多領(lǐng)域身份驗(yàn)證嘗試。在調(diào)用 Realms 進(jìn)行身份驗(yàn)證之前,期間或者之后,將調(diào)用 AuthenticationStrategy 以允許它對(duì)每個(gè) Realm 的結(jié)果做出反應(yīng)。
步驟5:將咨詢每個(gè) realm 以查看是否支持已提交的 AuthenticationToken。如果是這樣,將使用提交的 Token 調(diào)用 realm 的getauthenticationfo方法。getauthenticationfo方法有效地表示該特定領(lǐng)域的單一身份驗(yàn)證嘗試。
接下來(lái)就開(kāi)始使用ShiroAPI完成驗(yàn)證操作了。
環(huán)境準(zhǔn)備:
本文使用 Maven 構(gòu)建,因此需要一點(diǎn) Maven 知識(shí)。首先準(zhǔn)備環(huán)境依賴:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version> </dependency> </dependencies>
添加 junit 和 shiro-core 依賴即可。
初識(shí):登錄 / 退出
1、首先準(zhǔn)備一些用戶身份 / 憑據(jù)(shiro.ini)
[users] zhangsan=123 lisi=123
2、測(cè)試用例(com.luther.shiro.authenticator.AuthenticateTest)
/** * 底層默認(rèn)使用ModularRealmAuthenticator驗(yàn)證模塊(多realm驗(yàn)證) <br/> * AtLeastOneSuccessfulStrategy驗(yàn)證策略(只要有一個(gè)域成功進(jìn)行身份驗(yàn)證, * 則認(rèn)為成功。否則失敗。會(huì)返回所有成功的用戶標(biāo)識(shí)) * @author luther * @time 2019年7月5日 上午9:58:39 */ @Test public void testHelloWorld() { // 使用Ini配置文件初始化SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); // 得到SecurityManager實(shí)例 并綁定給SecurityUtils SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 得到Subject及創(chuàng)建用戶名/密碼身份驗(yàn)證Token(即用戶身份/憑證) Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123"); // 記住用戶 token.setRememberMe(Boolean.TRUE); try { // 登錄 subject.login(token); System.out.println("登錄成功"); } catch (AuthenticationException e) { System.err.println("登錄失敗,失敗原因:"); e.printStackTrace(); } assertTrue("用戶已驗(yàn)證成功", subject.isAuthenticated()); // 退出 subject.logout(); }
Realm
realm 接口結(jié)構(gòu)如下,其下方法有:
String getName(); //返回一個(gè)唯一的Realm名字 boolean supports(AuthenticationToken token); //判斷此Realm是否支持此Token AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException; //根據(jù)Token獲取認(rèn)證信息
其中主要默認(rèn)實(shí)現(xiàn)如下:
org.apache.shiro.realm.text.IniRealm:從ini文件獲取用戶權(quán)限等信息。
org.apache.shiro.realm.text.PropertiesRealm:從properties文件獲取用戶權(quán)限等信息。
org.apache.shiro.realm.jdbc.JdbcRealm:從數(shù)據(jù)庫(kù)獲取用戶權(quán)限等信息。
以后開(kāi)發(fā)一般繼承 AuthorizingRealm(授權(quán))抽象類即可;其繼承了 AuthenticatingRealm(即身份驗(yàn)證),而且也間接繼承了 CachingRealm(帶有緩存實(shí)現(xiàn))。
一、單 Realm 配置
1、自定義 Realm 實(shí)現(xiàn)(com.luther.shiro.realm.MyRealm1):
public class MyRealm1 implements Realm { @Override public String getName() { return this.getClass().getName(); } @Override public boolean supports(AuthenticationToken token) { // 只支持UsernamePasswordToken return token instanceof UsernamePasswordToken; } @Override public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; // 得到用戶名 String username = usernamePasswordToken.getUsername(); // 得到密碼 String password = new String(usernamePasswordToken.getPassword()); // 戶名錯(cuò)誤 if (!"zhangsan".equals(username)) { throw new UnknownAccountException(); } // 密碼錯(cuò)誤 if (!"123".equals(password)) { throw new IncorrectCredentialsException(); } System.out.println("用戶" + username + "驗(yàn)證成功"); // 返回 return new SimpleAccount(username, password, getName()); } }
2、ini 配置文件指定自定義 Realm 實(shí)現(xiàn) (shiro-realm.ini)
[main] #聲明一個(gè)realm myRealm1=com.luther.shiro.realm.MyRealm1 #指定securityManager的realms實(shí)現(xiàn),通過(guò) $name 來(lái)引入之前的 realm 定義 securityManager.realms=$myRealm1
3、測(cè)試用例請(qǐng)參考 com.luther.shiro.authenticator.AuthenticateTest 的testSingleRealm 測(cè)試方法,此方法和 testHelloWorld 除了配置文件其它沒(méi)有差異。
二、多 Realm 配置
1、ini 配置文件(shiro-multi-realm.ini)
[main] #聲明一個(gè)realm myRealm1=com.luther.shiro.realm.MyRealm1 #聲明一個(gè)realm myRealm2=com.luther.shiro.realm.MyRealm2 #指定securityManager的realms實(shí)現(xiàn),通過(guò) $name 來(lái)引入之前的 realm 定義 securityManager.realms=$myRealm2,$myRealm1 #此例子中顯視的指定了順序?yàn)閙yRealm2,myRealm1。(可以少指定,比如只指定myRealm2,則myRealm1會(huì)被忽略) #如果不指定securityManager.realms,則會(huì)按realm的聲明(無(wú)需設(shè)置 realms 屬性,其會(huì)自動(dòng)發(fā)現(xiàn))順序來(lái),此處即為myRealm1,myRealm2。
2、測(cè)試用例請(qǐng)參考 com.luther.shiro.authenticator.AuthenticateTest 的 testMutiRealm 測(cè)試方法。
Authenticator 及 AuthenticationStrategy
一、Authenticator 及 AuthenticationStrategy簡(jiǎn)介
Authenticator 的職責(zé)是驗(yàn)證用戶帳號(hào),是 Shiro API 中身份驗(yàn)證核心的入口點(diǎn)(默認(rèn)實(shí)現(xiàn)為 ModularRealmAuthenticator ),其方法定義為:
public AuthenticationInfo authenticate(AuthenticationToken authenticationToken) throws AuthenticationException;
如果驗(yàn)證成功,將返回 AuthenticationInfo 驗(yàn)證信息;此信息中包含了身份及憑證;如果驗(yàn)證失敗將拋出相應(yīng)的 AuthenticationException 實(shí)現(xiàn)。
AuthenticationStrategy 則是 Authenticator 進(jìn)行驗(yàn)證時(shí)的驗(yàn)證策略 (默認(rèn)實(shí)現(xiàn)為:AtLeastOneSuccessfulStrategy),Shiro API 自帶的策略有以下三種:
FirstSuccessfulStrategy:只要有一個(gè) Realm 驗(yàn)證成功即可,只返回第一個(gè) Realm 身份驗(yàn)證成功的認(rèn)證信息,其他的忽略。
AtLeastOneSuccessfulStrategy:只要有一個(gè) Realm 驗(yàn)證成功即可,和 FirstSuccessfulStrategy 不同,返回所有 Realm 身份驗(yàn)證成功的認(rèn)證信息。
AllSuccessfulStrategy:所有 Realm 驗(yàn)證成功才算成功,且返回所有 Realm 身份驗(yàn)證成功的認(rèn)證信息,如果有一個(gè)失敗就失敗了。
二、Authenticator 及 AuthenticationStrategy演示
假設(shè)我們有三個(gè) realm:
myRealm1: 用戶名/密碼為 zhangsan/123 時(shí)成功,且返回身份/憑據(jù)為 zhangsan/123;
myRealm2: 用戶名/密碼為 lisi/123 時(shí)成功,且返回身份/憑據(jù)為 lisi/123;
myRealm3: 用戶名/密碼為 zhangsan/123 時(shí)成功,且返回身份/憑據(jù)為 zhangsan.qq/123
1、通用化登錄邏輯
private void authentition(String iniConfigPath, String username, String password) { // 使用Ini配置文件初始化SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory(iniConfigPath); // 得到SecurityManager實(shí)例 并綁定給SecurityUtils SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 得到Subject及創(chuàng)建用戶名/密碼身份驗(yàn)證Token(即用戶身份/憑證) Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 記住用戶 token.setRememberMe(Boolean.TRUE); try { // 登錄 subject.login(token); System.out.println("用戶zhangsan登錄成功,其身份標(biāo)識(shí)為" + subject.getPrincipals()); } catch (AuthenticationException e) { System.err.println("登錄失敗,失敗原因:"); e.printStackTrace(); } assertTrue("用戶已驗(yàn)證成功", subject.isAuthenticated()); // 退出 subject.logout(); }
2、測(cè)試 AtLeastOneSuccessfulStrategy
2.1 ini 配置文件 (shiro-firstSuccessfulStrategy.ini)
[main] #指定securityManager的authenticator實(shí)現(xiàn),可以不指定,因?yàn)槠淠J(rèn)實(shí)現(xiàn)就是ModularRealmAuthenticator #authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator #securityManager.authenticator=$authenticator #指定securityManager.authenticator的authenticationStrategy,可以不指定,因?yàn)槠淠J(rèn)實(shí)現(xiàn)就是AtLeastOneSuccessfulStrategy #authenticationStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy #securityManager.authenticator.authenticationStrategy=$authenticationStrategy #聲明一個(gè)realm myRealm1=com.luther.shiro.realm.MyRealm1 #聲明一個(gè)realm myRealm2=com.luther.shiro.realm.MyRealm2 #聲明一個(gè)realm myRealm3=com.luther.shiro.realm.MyRealm3 #指定securityManager的realms實(shí)現(xiàn),通過(guò) $name 來(lái)引入之前的 realm 定義 securityManager.realms=$myRealm3,$myRealm2,$myRealm1
2.2 測(cè)試代碼
/** * 演示AtLeastOneSuccessfulStrategy的效果 * 每個(gè)realm都會(huì)進(jìn)行驗(yàn)證,并返回全部的成功用戶標(biāo)識(shí) * @author luther * @time 2019年7月5日 上午11:30:24 */ @Test public void testAtLeastOneSuccessfulStrategy() { authentition("classpath:shiro-atLeastOneSuccessfulStrategy.ini", "zhangsan", "123"); }
2.3 測(cè)試結(jié)果
開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm3 com.luther.shiro.realm.MyRealm3 - 用戶zhangsan驗(yàn)證成功 開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm2 開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm1 com.luther.shiro.realm.MyRealm1 - 用戶zhangsan驗(yàn)證成功 用戶zhangsan登錄成功,其身份標(biāo)識(shí)為zhangsan.qq,zhangsan
即 PrincipalCollection 包含了 zhangsan 和 zhangsan.qq 身份信息。
3、測(cè)試 AllSuccessfulStrategy
3.1 ini 配置文件 (shiro-allSuccessfulStrategy.ini)
[main] #指定securityManager的authenticator實(shí)現(xiàn),可以不指定,因?yàn)槠淠J(rèn)實(shí)現(xiàn)就是ModularRealmAuthenticator #authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator #securityManager.authenticator=$authenticator #指定securityManager.authenticator的authenticationStrategy為AllSuccessfulStrategy authenticationStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy securityManager.authenticator.authenticationStrategy=$authenticationStrategy #聲明一個(gè)realm myRealm1=com.luther.shiro.realm.MyRealm1 #聲明一個(gè)realm myRealm2=com.luther.shiro.realm.MyRealm2 #聲明一個(gè)realm myRealm3=com.luther.shiro.realm.MyRealm3 #指定securityManager的realms實(shí)現(xiàn),通過(guò) $name 來(lái)引入之前的 realm 定義 securityManager.realms=$myRealm3,$myRealm2,$myRealm1
3.2 測(cè)試代碼
/** * 演示AllSuccessfulStrategy的效果 * 依次對(duì)每個(gè)realm進(jìn)行驗(yàn)證,驗(yàn)證通過(guò)下一個(gè),驗(yàn)證失敗,直接返回失敗驗(yàn)證原因,只有全部成功時(shí)才返回全部的用戶標(biāo)識(shí) * 每個(gè)realm都會(huì)進(jìn)行驗(yàn)證,并返回全部的成功用戶標(biāo)識(shí) * @author luther * @time 2019年7月5日 上午11:30:24 */ @Test public void testAllSuccessfulStrategy() { authentition("classpath:shiro-allSuccessfulStrategy.ini", "zhangsan", "123"); }
3.3 測(cè)試結(jié)果
開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm3 com.luther.shiro.realm.MyRealm3 - 用戶zhangsan驗(yàn)證成功 開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm2 登錄失敗,失敗原因: org.apache.shiro.authc.UnknownAccountException at com.luther.shiro.realm.MyRealm2.getAuthenticationInfo(MyRealm2.java:42) ······
4、測(cè)試 firstSuccessfulStrategy
4.1 ini 配置文件 (shiro-firstSuccessfulStrategy.ini)
[main] #指定securityManager的authenticator實(shí)現(xiàn),可以不指定,因?yàn)槠淠J(rèn)實(shí)現(xiàn)就是ModularRealmAuthenticator #authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator #securityManager.authenticator=$authenticator #指定securityManager.authenticator的authenticationStrategy為FirstSuccessfulStrategy authenticationStrategy=org.apache.shiro.authc.pam.FirstSuccessfulStrategy securityManager.authenticator.authenticationStrategy=$authenticationStrategy #聲明一個(gè)realm myRealm1=com.luther.shiro.realm.MyRealm1 #聲明一個(gè)realm myRealm2=com.luther.shiro.realm.MyRealm2 #聲明一個(gè)realm myRealm3=com.luther.shiro.realm.MyRealm3 #指定securityManager的realms實(shí)現(xiàn),通過(guò) $name 來(lái)引入之前的 realm 定義 securityManager.realms=$myRealm3,$myRealm2,$myRealm1
4.2 測(cè)試代碼
/** * 演示FirstSuccessfulStrategy的效果 * 會(huì)對(duì)每個(gè)realm進(jìn)行驗(yàn)證,全部驗(yàn)證完后會(huì)返回驗(yàn)證成功的第一個(gè)用戶標(biāo)識(shí)(此處需注意,不是依次驗(yàn)證立馬返回,而是全部驗(yàn)證再返回) * @author luther * @time 2019年7月5日 上午11:30:24 */ @Test public void testFirstSuccessfulStrategy() { authentition("classpath:shiro-firstSuccessfulStrategy.ini", "zhangsan", "123"); }
4.3 測(cè)試結(jié)果
開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm3 com.luther.shiro.realm.MyRealm3 - 用戶zhangsan驗(yàn)證成功 開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm2 開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm1 com.luther.shiro.realm.MyRealm1 - 用戶zhangsan驗(yàn)證成功 用戶zhangsan登錄成功,其身份標(biāo)識(shí)為zhangsan.qq
以上已經(jīng)演示了API自帶的驗(yàn)證策略,以下稍微演示下自定義的驗(yàn)證策略。
自定義 AuthenticationStrategy 實(shí)現(xiàn)之前,首先簡(jiǎn)單看其 API:
//在所有Realm驗(yàn)證之前調(diào)用 AuthenticationInfo beforeAllAttempts( Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException; //在每個(gè)Realm之前調(diào)用 AuthenticationInfo beforeAttempt( Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException; //在每個(gè)Realm之后調(diào)用 AuthenticationInfo afterAttempt( Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException; //在所有Realm之后調(diào)用 AuthenticationInfo afterAllAttempts( AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException;
5、自定義驗(yàn)證策略(com.luther.shiro.authenticationStrategy.LastSuccessfulStrategy)
/** * 驗(yàn)證所有realm,并返回最后一個(gè)驗(yàn)證通過(guò)的身份標(biāo)識(shí) * @author luther * @time 2019年7月5日 下午4:12:31 */ public class LastSuccessfulStrategy extends AbstractAuthenticationStrategy { // 第一種方案修改afterAttempt方法 @Override public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException { return singleRealmInfo; } // // 第二種方案修改merge方法 // @Override // protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) { // if (info != null) { // aggregate = info; // } // // return aggregate; // } }
5.1 ini 配置文件 (shiro-lastSuccessfulStrategy.ini)
[main] #指定securityManager的authenticator實(shí)現(xiàn),可以不指定,因?yàn)槠淠J(rèn)實(shí)現(xiàn)就是ModularRealmAuthenticator #authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator #securityManager.authenticator=$authenticator #指定securityManager.authenticator的authenticationStrategy為自定義的LastSuccessfulStrategy authenticationStrategy=com.luther.shiro.authenticationStrategy.LastSuccessfulStrategy securityManager.authenticator.authenticationStrategy=$authenticationStrategy #聲明一個(gè)realm myRealm1=com.luther.shiro.realm.MyRealm1 #聲明一個(gè)realm myRealm2=com.luther.shiro.realm.MyRealm2 #聲明一個(gè)realm myRealm3=com.luther.shiro.realm.MyRealm3 #指定securityManager的realms實(shí)現(xiàn),通過(guò) $name 來(lái)引入之前的 realm 定義 securityManager.realms=$myRealm3,$myRealm2,$myRealm1
5.2 測(cè)試代碼
/** * 演示自定義的LastSuccessfulStrategy的效果 * 會(huì)對(duì)每個(gè)realm進(jìn)行驗(yàn)證,全部驗(yàn)證完后會(huì)返回驗(yàn)證成功的最后一個(gè)用戶標(biāo)識(shí) * @author luther * @time 2019年7月5日 上午11:30:24 */ @Test public void testLastSuccessfulStrategy() { authentition("classpath:shiro-lastSuccessfulStrategy.ini", "zhangsan", "123"); }
5.3 測(cè)試結(jié)果
開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm3 com.luther.shiro.realm.MyRealm3 - 用戶zhangsan驗(yàn)證成功 開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm2 開(kāi)始驗(yàn)證com.luther.shiro.realm.MyRealm1 com.luther.shiro.realm.MyRealm1 - 用戶zhangsan驗(yàn)證成功 用戶zhangsan登錄成功,其身份標(biāo)識(shí)為zhangsan
上述就是小編為大家分享的什么是Shiro驗(yàn)證了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。