Shiro源码学习之二

接上一篇 Shiro源码学习之一

3.subject.login

Shiro源码学习之二_第1张图片


进入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;

Shiro源码学习之二_第2张图片

回到clearRunAsIdentitiesInternal



回到login

Shiro源码学习之二_第3张图片


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

Shiro源码学习之二_第4张图片


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

java transient简介


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

Shiro源码学习之二_第5张图片


进入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;
    }

Shiro源码学习之二_第6张图片


进入protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)


protected void assertRealmsConfigured() throws IllegalStateException {

Shiro源码学习之二_第7张图片


IllegalStateException异常:

Shiro源码学习之二_第8张图片

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


回到doAuthenticate

Shiro源码学习之二_第9张图片


进入doSingleRealmAuthentication

Shiro源码学习之二_第10张图片



进入getCachedAuthenticationInfo

Shiro源码学习之二_第11张图片

getAvailableAuthenticationCache返回null



回到getCachedAuthenticationInfo返回info是null

Shiro源码学习之二_第12张图片

回到getAuthenticationInfo


进入doGetAuthenticationInfo



进入getUser用到了lock与unlock

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

Shiro源码学习之二_第13张图片

Shiro源码学习之二_第14张图片

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


Shiro源码学习之二_第15张图片

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

Shiro源码学习之二_第16张图片

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
Shiro源码学习之二_第17张图片

进入assertCredentialsMatch

Shiro源码学习之二_第18张图片

进入doCredentialsMatch

Shiro源码学习之二_第19张图片

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

Shiro源码学习之二_第20张图片

顺便提到一点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

Shiro源码学习之二_第21张图片

回到protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)

Shiro源码学习之二_第22张图片

回到public final AuthenticationInfo authenticate(AuthenticationToken token)

Shiro源码学习之二_第23张图片

代码走到notifySuccess(token, info);



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



回到public final AuthenticationInfo authenticate(AuthenticationToken token)

Shiro源码学习之二_第24张图片

回到public AuthenticationInfo authenticate(AuthenticationToken token)



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

Shiro源码学习之二_第25张图片

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


进入public Subject createSubject(SubjectContext subjectContext)

Shiro源码学习之二_第26张图片

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

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


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

Shiro源码学习之二_第27张图片

回到public Subject createSubject(SubjectContext subjectContext)

Shiro源码学习之二_第28张图片

protected SubjectContext ensureSecurityManager(SubjectContext context)

Shiro源码学习之二_第29张图片

回到public Subject createSubject(SubjectContext subjectContext)

Shiro源码学习之二_第30张图片

进入protected SubjectContext resolveSession(SubjectContext context)

Shiro源码学习之二_第31张图片

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)

Shiro源码学习之二_第32张图片

又回到public Subject createSubject(SubjectContext subjectContext)

Shiro源码学习之二_第33张图片


进入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)

Shiro源码学习之二_第34张图片

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


protected void save(Subject subject) {
        this.subjectDAO.save(subject);
    }
Shiro源码学习之二_第35张图片

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)

Shiro源码学习之二_第36张图片


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

Shiro源码学习之二_第37张图片

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)

Shiro源码学习之二_第38张图片

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

Shiro源码学习之二_第39张图片

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

参考:HashMap和ConcurrentHashMap研究

回到protected void mergeAuthenticationState(Subject subject)


回到saveToSession

回到public Subject save(Subject subject)

Shiro源码学习之二_第40张图片

回到protected void save(Subject subject)


回到public Subject createSubject(SubjectContext subjectContext)

Shiro源码学习之二_第41张图片

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

Shiro源码学习之二_第42张图片

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

Shiro源码学习之二_第43张图片

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

Shiro源码学习之二_第44张图片

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

RememberMeManager rmm =null直接返回了


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

Shiro源码学习之二_第45张图片

回到外层public void login(AuthenticationToken token)

Shiro源码学习之二_第46张图片

继续往下

Shiro源码学习之二_第47张图片

进入protected Session decorate(Session session)

Shiro源码学习之二_第48张图片

进入private StoppingAwareProxiedSession(Session target, DelegatingSubject owningSubject)


回到public void login(AuthenticationToken token)


终于回到最外层调用代码

Shiro源码学习之二_第49张图片


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);
    }

Shiro源码学习之二_第50张图片

调用系统级的remove方法。



你可能感兴趣的:(Shiro源码学习之二)