Shiro使用とソース分析---7

15770 ワード

Realm相関ソース分析
前章の解析ではRealmのsupports関数とgetAuthenticationInfoについて述べたが,以下で見る.4章の冒頭のSpring構成を参照して,最後に得られたのはAuthorizingRealmである.まずAuthorizingRealmの継承関係を見てみましょう.
public abstract class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware
public abstract class AuthenticatingRealm extends CachingRealm implements Initializable
public abstract class CachingRealm implements Realm, Nameable, CacheManagerAware, LogoutAware

AuthorizingRealm
まずAuthorizingRealmの構造関数を見てみましょう
    public AuthorizingRealm() {
        this(null, null);
    }
    public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
        super();
        if (cacheManager != null) setCacheManager(cacheManager);
        if (matcher != null) setCredentialsMatcher(matcher);

        this.authorizationCachingEnabled = true;
        this.permissionResolver = new WildcardPermissionResolver();

        int instanceNumber = INSTANCE_COUNT.getAndIncrement();
        this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
        if (instanceNumber > 0) {
            this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber;
        }
    }

AuthorizingRealmは主にWildcardPermissionResolverを構築し、このコンストラクション関数は空の関数である.AuthorizingRealmの親AuthenticatingRealmのコンストラクション関数を以下に示します.
    public AuthenticatingRealm() {
        this(null, new SimpleCredentialsMatcher());
    }
    public AuthenticatingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
        authenticationTokenClass = UsernamePasswordToken.class;

        this.authenticationCachingEnabled = false;

        int instanceNumber = INSTANCE_COUNT.getAndIncrement();
        this.authenticationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
        if (instanceNumber > 0) {
            this.authenticationCacheName = this.authenticationCacheName + "." + instanceNumber;
        }

        if (cacheManager != null) {
            setCacheManager(cacheManager);
        }
        if (matcher != null) {
            setCredentialsMatcher(matcher);
        }
    }

このコンストラクション関数も簡単な付与であり,ここでは解析しない.
createToken
次に、requestとresponseによってAuthenticationTokenを構築し、
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        String username = getUsername(request);
        String password = getPassword(request);
        return createToken(username, password, request, response);
    }

getUsernameとgetPasswordはrequestのパラメータを取得します.ここでcreateTokenはAuthenticatingFilterで定義されています.
    protected AuthenticationToken createToken(String username, String password,
                                              ServletRequest request, ServletResponse response) {
        boolean rememberMe = isRememberMe(request);
        String host = getHost(request);
        return createToken(username, password, rememberMe, host);
    }

isRememberMeはFormAuthenticationFilterで定義されています.
    protected boolean isRememberMe(ServletRequest request) {
        return WebUtils.isTrue(request, getRememberMeParam());
    }

このcreateTokenはAuthenticatingFilterで定義されています.
    protected AuthenticationToken createToken(String username, String password,
                                              boolean rememberMe, String host) {
        return new UsernamePasswordToken(username, password, rememberMe, host);
    }

UsernamePasswordTokenのコンストラクション関数は簡単な値です.
supports
    public boolean supports(AuthenticationToken token) {
        return token != null && getAuthenticationTokenClass().isAssignableFrom(token.getClass());
    }

get AuthenticationTokenClassは、AuthenticatingRealmコンストラクション関数の前のUsernamePasswordTokenを返し、isAssignableFromはtokenがUsernamePasswordTokenのインスタンスであるかどうかを判断するために使用されます.
getAuthenticationInfo
    public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        AuthenticationInfo info = getCachedAuthenticationInfo(token);
        if (info == null) {
            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;
    }

ここではまずgetCachedAuthenticationInfoを呼び出してキャッシュからログイン情報を取得し、キャッシュにない場合はdoGetAuthenticationInfoを呼び出して検証を行い、検証が成功したと仮定してcacheAuthenticationInfoIfPossibleとassertCredentialsMatchを呼び出す.次は一つ一つ分析します.
getCachedAuthenticationInfo
    private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token) {
        AuthenticationInfo info = null;

        Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
        if (cache != null && token != null) {
            log.trace("Attempting to retrieve the AuthenticationInfo from cache.");
            Object key = getAuthenticationCacheKey(token);
            info = cache.get(key);
            if (info == null) {
                log.trace("No AuthorizationInfo found in cache for key [{}]", key);
            } else {
                log.trace("Found cached AuthorizationInfo for key [{}]", key);
            }
        }

        return info;
    }

ここではまずキャッシュcacheを取得し、getAuthenticationCacheKeyによってKeyを計算し、キャッシュからログイン情報を抽出する.実は前のデフォルトのコンストラクション関数では、cacheはnullです.getAuthenticationCacheKey getPrincipal()でKeyを取得し、
    protected Object getAuthenticationCacheKey(AuthenticationToken token) {
        return token != null ? token.getPrincipal() : null;
    }

doGetAuthenticationInfo
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

doGetAuthenticationInfoは定義されていないので、実際のプロジェクトでは、AuthorizingRealmから継承されたRealmをカスタマイズして実装する必要があります.
cacheAuthenticationInfoIfPossible
    private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, AuthenticationInfo info) {
        if (!isAuthenticationCachingEnabled(token, info)) {
            log.debug("AuthenticationInfo caching is disabled for info [{}].  Submitted token: [{}].", info, token);
            return;
        }

        Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
        if (cache != null) {
            Object key = getAuthenticationCacheKey(token);
            cache.put(key, info);
            log.trace("Cached AuthenticationInfo for continued authentication.  key=[{}], value=[{}].", key, info);
        }
    }

この関数は,先ほどのログイン情報をキャッシュに追加し,次回検証するときに直接キャッシュからログイン情報を抽出する.
assertCredentialsMatch
    protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
        CredentialsMatcher cm = getCredentialsMatcher();
        if (cm != null) {
            if (!cm.doCredentialsMatch(token, info)) {
                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.");
        }
    }

getCredentialsMatcherは、AuthenticatingRealmコンストラクタの前にあるSimpleCredentialsMatcherを返します.doCredentialsMatchは次のようになります.
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        Object tokenCredentials = getCredentials(token);
        Object accountCredentials = getCredentials(info);
        return equals(tokenCredentials, accountCredentials);
    }

ここでは、両者のcredentialが等しいかどうかを検証します.