国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

Shiro

2019-11-14 22:04:51
字體:
供稿:網(wǎng)友
Shiro - 關(guān)于Realm

之前在Authentication和Authorization中也提到Realm。

無論是身份驗(yàn)證還是權(quán)限驗(yàn)證,無論數(shù)據(jù)以什么方式存在,我們都需要訪問一些數(shù)據(jù)并將其轉(zhuǎn)換為Shiro可以識別的格式。

通常一個數(shù)據(jù)源對應(yīng)一個Realm。因此,實(shí)現(xiàn)一個Realm時會用到該數(shù)據(jù)源相關(guān)的API。

通常一個數(shù)據(jù)源中會同時保存身份相關(guān)數(shù)據(jù)與權(quán)限相關(guān)數(shù)據(jù)。因此,一個Realm實(shí)現(xiàn)類可以進(jìn)行認(rèn)證和授權(quán)兩種操作。

可以將Realm簡單地理解為DAO。

(雖然IDEA生成的type hirarchy diagram很漂亮,但是太大了...還是用回eclipse截圖..)

如果使用.ini配置,我們可以在[main]部分定義N個Realm,但我們可以用顯式(explicit)和隱式(implicit)兩種方式為securityManager指定Realm(有點(diǎn)IOC容器的意思)。

顯示指定就是常見的方式,即定義Realm后再為securityManager按需要的順序指定Realm。

fooRealm = com.company.foo.RealmbarRealm = com.company.another.RealmbazRealm = com.company.baz.RealmsecurityManager.realms = $fooRealm, $barRealm, $bazRealm

顯示指定的方式相對較為明確,即使不改變Realm的定義,我們?nèi)钥梢宰岒?yàn)證和授權(quán)按我們給securityManager指定的順序的Realm來執(zhí)行。

如果因?yàn)槟承┰?可能是定義的Realm太多?)不想為securityManager.realms指定,我們也可以使用隱式方式。

也就是說,把上面的配置改成如下形式就是隱式方式了:

blahRealm = com.company.blah.RealmfooRealm = com.company.foo.RealmbarRealm = com.company.another.Realm

隱式方式其實(shí)就是不指定,只定義(define),Shiro會搜索配置中所有的Realm并將它們一一指定給securityManager。

使用隱式方式時只要稍微改一下Realm的定義,Shiro就可能會給我們來個驚喜。

在介紹Authentication的文章中,說的是當(dāng)一個驗(yàn)證請求出現(xiàn)時Shiro框架的工作流程。

在這里具體記錄一下Realm負(fù)責(zé)的工作(雖然說securiyManager驗(yàn)證開始的地方,但從數(shù)據(jù)源取數(shù)據(jù)并作比較的工作是由Realm來進(jìn)行的)。

以ModularRealmAuthenticator為例:

   PRotected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {        AuthenticationStrategy strategy = getAuthenticationStrategy();        AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);        if (log.isTraceEnabled()) {            log.trace("Iterating through {} realms for PAM authentication", realms.size());        }        for (Realm realm : realms) {            aggregate = strategy.beforeAttempt(realm, token, aggregate);            if (realm.supports(token)) {                log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);                AuthenticationInfo info = null;                Throwable t = null;                try {                    info = realm.getAuthenticationInfo(token);                } catch (Throwable throwable) {                    t = throwable;                    if (log.isDebugEnabled()) {                        String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";                        log.debug(msg, t);                    }                }                aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);            } else {                log.debug("Realm [{}] does not support token {}.  Skipping realm.", realm, token);            }        }        aggregate = strategy.afterAllAttempts(token, aggregate);        return aggregate;    }

在Realm開始處理驗(yàn)證的邏輯之前,Authenticator將調(diào)用Realm的supports方法去驗(yàn)證當(dāng)前Realm是否支持獲得的AuthenticationToken。

通常,Realm檢查的是token的類型,比如在AuthenticatingRealm中檢查類型是否相同。

public boolean supports(AuthenticationToken token) {    return token != null && getAuthenticationTokenClass().isAssignableFrom(token.getClass());}

另外,AuthenticatingRealm的constructor中類型默認(rèn)為

authenticationTokenClass = UsernamePassWordToken.class;

如果當(dāng)前Realm支持提交過來的token,authenticator則調(diào)用getAuthenticationInfo(token)方法。

以AuthenticatingRealm為例(注意是final):

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {    AuthenticationInfo info = getCachedAuthenticationInfo(token);    if (info == null) {        //otherwise not cached, perform the lookup:        info = doGetAuthenticationInfo(token);        log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);        if (token != null && info != null) {            cacheAuthenticationInfoIfPossible(token, info);        }    } else {        log.debug("Using cached authentication info [{}] to perform credentials matching.", info);    }    if (info != null) {        assertCredentialsMatch(token, info);    } else {        log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);    }    return info;}

如果可以從緩存中獲得驗(yàn)證信息,下一步則檢查密碼是否匹配,即assertCredentialsMatch(token, info)。

如果緩存中不存在驗(yàn)證信息則調(diào)用以下方法。

protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

這里我們暫時先不考慮緩存的情況,考慮doGetAuthenticationInfo應(yīng)該做什么。

有些人(比如我)直接在該方法中完成也驗(yàn)證,驗(yàn)證通過時返回SimpleAuthenticationInfo實(shí)例,失敗則拋出相應(yīng)的驗(yàn)證異常。

但下面有個assertCredentialsMatch,說明doGetAuthenticationInfo本沒有打算這樣用,這種使用方式會讓CredentialMatcher失去意義。

參考JdbcRealm的實(shí)現(xiàn),只是根據(jù)身份(用戶名)去查詢并返回SimpleAuthenticationInfo實(shí)例。

然后讓assertCredentialsMatch比較token和authenticationInfo。

protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {    CredentialsMatcher cm = getCredentialsMatcher();    if (cm != null) {        if (!cm.doCredentialsMatch(token, info)) {            //not successful - throw an exception to indicate this:            String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";            throw new IncorrectCredentialsException(msg);        }    } else {        throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +                "credentials during authentication.  If you do not wish for credentials to be examined, you " +                "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");    }}

既然提到了CredentialMatcher,我們來看看他的意義所在。 首先說明,AuthenticatingRealm的默認(rèn)CredentialMatcher是...

public AuthenticatingRealm() {    this(null, new SimpleCredentialsMatcher());}

如果僅僅是做密碼字符比較我們大可不必做出這樣一個接口(字符串比較的可插拔+可定制么?)

之前在說Authentication的時候就提過,AuthenticationToken的Principal和Credential可以是任何類型的,光是拿過來直接比較是否相符也不只是比較密碼字符那么簡單了。

SimpleCredentialsMatcher就是用來比較兩個credential是否相同的。

其doCredentialsMatch方法返回其equals方法的返回值。

protected boolean equals(Object tokenCredentials, Object accountCredentials) {    if (log.isDebugEnabled()) {        log.debug("Performing credentials equality check for tokenCredentials of type [" +                tokenCredentials.getClass().getName() + " and accountCredentials of type [" +                accountCredentials.getClass().getName() + "]");    }    if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) {        if (log.isDebugEnabled()) {            log.debug("Both credentials arguments can be easily converted to byte arrays.  Performing " +                    "array equals comparison");        }        byte[] tokenBytes = toBytes(tokenCredentials);        byte[] accountBytes = toBytes(accountCredentials);        return Arrays.equals(tokenBytes, accountBytes);    } else {        return accountCredentials.equals(tokenCredentials);    }}

當(dāng)然,實(shí)現(xiàn)類不只是SimpleCredentialsMatcher...

SimpleCredentialsMatcher下還跟著HashedCredentialsMatcher,再往下就都deprecated了。

說到HashedCredentialsMatcher,他只是給密碼加個salt以提高安全。

其中hashSalted屬性基本不用考慮,因?yàn)閺腟hiro 1.1開始salt是根據(jù)SaltedAuthenticationInfo的getCredentialSalt()方法返回的non-null value。

public HashedCredentialsMatcher() {    this.hashAlgorithm = null;    this.hashSalted = false;    this.hashIterations = 1;    this.storedCredentialsHexEncoded = true; //false means Base64-encoded}

說到salting就不得不說SaltedAuthenticationInfo,該接口繼承AuthenticationInfo,即除了principal和credential,他還有一個credentialsSalt。

public interface SaltedAuthenticationInfo extends AuthenticationInfo {    /**     * Returns the salt used to salt the account's credentials or {@code null} if no salt was used.     *     * @return the salt used to salt the account's credentials or {@code null} if no salt was used.     */    ByteSource getCredentialsSalt();}

HashedCredentialsMatcher有個默認(rèn)的salt,是將自己的principal作為salt,后來這個方法也被deprecated了。

因?yàn)閺?.1開始,Shiro禁止salt從用戶的登錄信息中獲取,而應(yīng)該從數(shù)據(jù)源獲取。

@Deprecatedprotected Object getSalt(AuthenticationToken token) {    return token.getPrincipal();}

這一系列方法和屬性會從Shiro 2.0開始徹底消失。


上一篇:Redis

下一篇:設(shè)計(jì)模式——代理

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 黔西县| 绥芬河市| 景德镇市| 融水| 富阳市| 太白县| 米易县| 博罗县| 保靖县| 浦江县| 昆山市| 天台县| 南陵县| 德兴市| 宝山区| 平度市| 西昌市| 中卫市| 灌云县| 怀仁县| 阿尔山市| 永春县| 西宁市| 黔东| 彭泽县| 集贤县| 吉安市| 象州县| 周口市| 崇义县| 汤阴县| 临邑县| 吴江市| 江安县| 即墨市| 泰来县| 湾仔区| 徐闻县| 牙克石市| 海原县| 综艺|