Shiro源码学习之二

接上一篇 Shiro源码学习之一

3.subject.login



进入login

public void login(AuthenticationToken token) throws AuthenticationException {
        clearRunAsIdentitiesInternal();
        Subject subject = securityManager.login(this, token);

        PrincipalCollection principals;

        String host = null;

        if (subject instanceof DelegatingSubject) {
            DelegatingSubject delegating = (DelegatingSubject) subject;
            //we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:
            principals = delegating.principals;
            host = delegating.host;
        } else {
            principals = subject.getPrincipals();
        }

        if (principals == null || principals.isEmpty()) {
            String msg = "Principals returned from securityManager.login( token ) returned a null or " +
                    "empty value.  This value must be non null and populated with one or more elements.";
            throw new IllegalStateException(msg);
        }
        this.principals = principals;
        this.authenticated = true;
        if (token instanceof HostAuthenticationToken) {
            host = ((HostAuthenticationToken) token).getHost();
        }
        if (host != null) {
            this.host = host;
        }
        Session session = subject.getSession(false);
        if (session != null) {
            this.session = decorate(session);
        } else {
            this.session = null;
        }
    }

private void clearRunAsIdentitiesInternal() {
        //try/catch added for SHIRO-298
        try {
            clearRunAsIdentities();
        } catch (SessionException se) {
            log.debug("Encountered session exception trying to clear 'runAs' identities during logout.  This " +
                    "can generally safely be ignored.", se);
        }
    }

private void clearRunAsIdentities() {
        Session session = getSession(false);
        if (session != null) {
            session.removeAttribute(RUN_AS_PRINCIPALS_SESSION_KEY);
        }
    }


Session接口
public interface Session {
Serializable getId();
...


第一次直接return null

public Session getSession(boolean create) {
...
if (this.session == null && create) {
...
SessionContext sessionContext = createSessionContext();
            Session session = this.securityManager.start(sessionContext);
            this.session = decorate(session);
        }
        return this.session;

回到clearRunAsIdentitiesInternal



回到login



securityManager是DelegatingSubject声明的protected transient SecurityManager无序序列化



java的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

java transient简介


进入public Subject login(Subject subject, AuthenticationToken token)



进入public AuthenticationInfo authenticate(AuthenticationToken token)

public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
        return this.authenticator.authenticate(token);
    }


进入public final AuthenticationInfo authenticate(AuthenticationToken token)

public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {

        if (token == null) {
            throw new IllegalArgumentException("Method argumet (authentication token) cannot be null.");
        }

        log.trace("Authentication attempt received for token [{}]", token);

        AuthenticationInfo info;
        try {
            info = doAuthenticate(token);
            if (info == null) {
                String msg = "No account information found for authentication token [" + token + "] by this " +
                        "Authenticator instance.  Please check that it is configured correctly.";
                throw new AuthenticationException(msg);
            }
        } catch (Throwable t) {
            AuthenticationException ae = null;
            if (t instanceof AuthenticationException) {
                ae = (AuthenticationException) t;
            }
            if (ae == null) {
                //Exception thrown was not an expected AuthenticationException.  Therefore it is probably a little more
                //severe or unexpected.  So, wrap in an AuthenticationException, log to warn, and propagate:
                String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected " +
                        "error? (Typical or expected login exceptions should extend from AuthenticationException).";
                ae = new AuthenticationException(msg, t);
            }
            try {
                notifyFailure(token, ae);
            } catch (Throwable t2) {
                if (log.isWarnEnabled()) {
                    String msg = "Unable to send notification for failed authentication attempt - listener error?.  " +
                            "Please check your AuthenticationListener implementation(s).  Logging sending exception " +
                            "and propagating original AuthenticationException instead...";
                    log.warn(msg, t2);
                }
            }


            throw ae;
        }

        log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);

        notifySuccess(token, info);

        return info;
    }



进入protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)


protected void assertRealmsConfigured() throws IllegalStateException {



IllegalStateException异常:


protected Collection<Realm> getRealms() {
        return this.realms;
    }


回到doAuthenticate



进入doSingleRealmAuthentication




进入getCachedAuthenticationInfo


getAvailableAuthenticationCache返回null



回到getCachedAuthenticationInfo返回info是null

回到getAuthenticationInfo


进入doGetAuthenticationInfo



进入getUser用到了lock与unlock

USERS_LOCK.readLock().lock();
        try {
            return this.users.get(username);
        } finally {
            USERS_LOCK.readLock().unlock();
        }



参考:Java中锁的应用之-Lock



public boolean isCredentialsExpired() {
        return credentialsExpired;
    }
account.isCredentialsExpired()返回flase

doGetAuthenticationInfo返回account


回到getAuthenticationInfo

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 quietly, caching is disabled for this token/info pair:
            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);
        }
    }
回到getAuthenticationInfo

进入assertCredentialsMatch

进入doCredentialsMatch

进入protected boolean equals(Object tokenCredentials, Object accountCredentials)

顺便提到一点public static boolean equals(byte[] a, byte[] a2)已经是java.util的class Arrays了

public static boolean equals(byte[] a, byte[] a2) {
        if (a==a2)
            return true;
        if (a==null || a2==null)
            return false;

        int length = a.length;
        if (a2.length != length)
            return false;

        for (int i=0; i<length; i++)
            if (a[i] != a2[i])
                return false;

        return true;
    }

回到getAuthenticationInfo


回到doSingleRealmAuthentication


回到protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)


回到public final AuthenticationInfo authenticate(AuthenticationToken token)


代码走到notifySuccess(token, info);



protected void notifySuccess(AuthenticationToken token, AuthenticationInfo info)因为listeners的size=0所以直接跳出此方法



回到public final AuthenticationInfo authenticate(AuthenticationToken token)


回到public AuthenticationInfo authenticate(AuthenticationToken token)



回到public Subject login(Subject subject, AuthenticationToken token)


进入protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing)


进入public Subject createSubject(SubjectContext subjectContext)


protected SubjectContext copy(SubjectContext subjectContext) {
        return new DefaultSubjectContext(subjectContext);
    }

public DefaultSubjectContext(SubjectContext ctx) {
        super(ctx);
    }


运行到this.backingMap.putAll(map);


回到public Subject createSubject(SubjectContext subjectContext)


protected SubjectContext ensureSecurityManager(SubjectContext context)


回到public Subject createSubject(SubjectContext subjectContext)


进入protected SubjectContext resolveSession(SubjectContext context)


protected SessionKey getSessionKey(SubjectContext context) {
        Serializable sessionId = context.getSessionId();
        if (sessionId != null) {
            return new DefaultSessionKey(sessionId);
        }
        return null;
    }
Serializable sessionId =null直接返回。

回到protected SubjectContext resolveSession(SubjectContext context)


又回到public Subject createSubject(SubjectContext subjectContext)



进入protected SubjectContext resolvePrincipals(SubjectContext context)由于principals不为空直接返回了


 @SuppressWarnings({"unchecked"})
    protected SubjectContext resolvePrincipals(SubjectContext context) {

        PrincipalCollection principals = context.resolvePrincipals();

        if (CollectionUtils.isEmpty(principals)) {
            log.trace("No identity (PrincipalCollection) found in the context.  Looking for a remembered identity.");

            principals = getRememberedIdentity(context);

            if (!CollectionUtils.isEmpty(principals)) {
                log.debug("Found remembered PrincipalCollection.  Adding to the context to be used " +
                        "for subject construction by the SubjectFactory.");

                context.setPrincipals(principals);
				} else {
                log.trace("No remembered identity found.  Returning original context.");
            }
        }

        return context;
    }


又回到public Subject createSubject(SubjectContext subjectContext) 


protected Subject doCreateSubject(SubjectContext context) {
        return getSubjectFactory().createSubject(context);
    }
public SubjectFactory getSubjectFactory() {
        return subjectFactory;
    }


进入public Subject createSubject(SubjectContext context)

public DelegatingSubject(PrincipalCollection principals, boolean authenticated, String host,Session session, boolean sessionCreationEnabled, SecurityManager securityManager)


回到createSubject,回到doCreateSubject,最后回到public Subject createSubject(SubjectContext subjectContext)


protected void save(Subject subject) {
        this.subjectDAO.save(subject);
    }

protected void saveToSession(Subject subject) {
        //performs merge logic, only updating the Subject's session if it does not match the current state:
        mergePrincipals(subject);
        mergeAuthenticationState(subject);
    }
protected void mergePrincipals(Subject subject) {
        //merge PrincipalCollection state:

        PrincipalCollection currentPrincipals = null;

        //SHIRO-380: added if/else block - need to retain original (source) principals
        //This technique (reflection) is only temporary - a proper long term solution needs to be found,
        //but this technique allowed an immediate fix that is API point-version forwards and backwards compatible
        //
        //A more comprehensive review / cleaning of runAs should be performed for Shiro 1.3 / 2.0 +
        if (subject.isRunAs() && subject instanceof DelegatingSubject) {
            try {
                Field field = DelegatingSubject.class.getDeclaredField("principals");
                field.setAccessible(true);
                currentPrincipals = (PrincipalCollection)field.get(subject);
            } catch (Exception e) {
                throw new IllegalStateException("Unable to access DelegatingSubject principals property.", e);
            }
        }
        if (currentPrincipals == null || currentPrincipals.isEmpty()) {
            currentPrincipals = subject.getPrincipals();
        }

        Session session = subject.getSession(false);

        if (session == null) {
            if (!CollectionUtils.isEmpty(currentPrincipals)) {
                session = subject.getSession();
                session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);
            }
            //otherwise no session and no principals - nothing to save
        } else {
            PrincipalCollection existingPrincipals =
                    (PrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);

            if (CollectionUtils.isEmpty(currentPrincipals)) {
                if (!CollectionUtils.isEmpty(existingPrincipals)) {
                    session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
                }
                //otherwise both are null or empty - no need to update the session
            } else {
                if (!currentPrincipals.equals(existingPrincipals)) {
                    session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);
                }
                //otherwise they're the same - no need to update the session
            }
        }
    }
protected void mergeAuthenticationState(Subject subject) {

        Session session = subject.getSession(false);

        if (session == null) {
            if (subject.isAuthenticated()) {
                session = subject.getSession();
                session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);
            }
            //otherwise no session and not authenticated - nothing to save
        } else {
            Boolean existingAuthc = (Boolean) session.getAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);

            if (subject.isAuthenticated()) {
                if (existingAuthc == null || !existingAuthc) {
                    session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);
                }
                //otherwise authc state matches - no need to update the session
            } else {
                if (existingAuthc != null) {
                    //existing doesn't match the current state - remove it:
                    session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
                }
                //otherwise not in the session and not authenticated - no need to update the session
            }
        }
    }
public void setAttribute(Object attributeKey, Object value)




private Session lookupSession(SessionKey key) throws SessionException {
        if (key == null) {
            throw new NullPointerException("SessionKey argument cannot be null.");
        }
        return doGetSession(key);
    }


protected void validate(Session session, SessionKey key) throws InvalidSessionException {
        try {
            doValidate(session);
        } catch (ExpiredSessionException ese) {
            onExpiration(session, ese, key);
            throw ese;
        } catch (InvalidSessionException ise) {
            onInvalidation(session, ise, key);
            throw ise;
        }
    }
protected void doValidate(Session session) throws InvalidSessionException {
        if (session instanceof ValidatingSession) {
            ((ValidatingSession) session).validate();
        } else {
            String msg = "The " + getClass().getName() + " implementation only supports validating " +
                    "Session implementations of the " + ValidatingSession.class.getName() + " interface.  " +
                    "Please either implement this interface in your session implementation or override the " +
                    AbstractValidatingSessionManager.class.getName() + ".doValidate(Session) method to perform validation.";
            throw new IllegalStateException(msg);
        }
    }

回到public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value)


protected void onChange(Session session) {
        sessionDAO.update(session);
    }
public void update(Session session) throws UnknownSessionException {
        storeSession(session.getId(), session);
    }

这已经是系统级的代码了java.util.concurrent

参考:HashMap和ConcurrentHashMap研究

回到protected void mergeAuthenticationState(Subject subject)


回到saveToSession

回到public Subject save(Subject subject)


回到protected void save(Subject subject)


回到public Subject createSubject(SubjectContext subjectContext)


回到protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing)


回到public Subject login(Subject subject, AuthenticationToken token) 


进入protected void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject)


进入protected void rememberMeSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject)

RememberMeManager rmm =null直接返回了


又回到public Subject login(Subject subject, AuthenticationToken token)


回到外层public void login(AuthenticationToken token)


继续往下


进入protected Session decorate(Session session)


进入private StoppingAwareProxiedSession(Session target, DelegatingSubject owningSubject)


回到public void login(AuthenticationToken token)


终于回到最外层调用代码



4.subject.getPrincipal()

logger.info("登录成功!Hello " + subject.getPrincipal());
public Object getPrincipal() {
        return getPrimaryPrincipal(getPrincipals());
    }


5.subject.logout();

public void logout() {
        try {
            clearRunAsIdentitiesInternal();
            this.securityManager.logout(this);
        } finally {
            this.session = null;
            this.principals = null;
            this.authenticated = false;
            //Don't set securityManager to null here - the Subject can still be
            //used, it is just considered anonymous at this point.  The SecurityManager instance is
            //necessary if the subject would log in again or acquire a new session.  This is in response to
            //https://issues.apache.org/jira/browse/JSEC-22
            //this.securityManager = null;
        }
    }
private void clearRunAsIdentitiesInternal() {
        //try/catch added for SHIRO-298
        try {
            clearRunAsIdentities();
        } catch (SessionException se) {
            log.debug("Encountered session exception trying to clear 'runAs' identities during logout.  This " +
                    "can generally safely be ignored.", se);
        }
    }
private void clearRunAsIdentities() {
        Session session = getSession(false);
        if (session != null) {
            session.removeAttribute(RUN_AS_PRINCIPALS_SESSION_KEY);
        }
    }
public Object removeAttribute(Object key) throws InvalidSessionException {
        return delegate.removeAttribute(key);
    }
public Object removeAttribute(Object attributeKey) throws InvalidSessionException {
        return sessionManager.removeAttribute(this.key, attributeKey);
    }

调用系统级的remove方法。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值