Detailed entry process shiro source

Foreword

Shiro extract the most basic entry process (web landing layer is based on this development).
Detailed source

AuthenticationToken create a log.

SecurityUtils.getSecurityManager().login(null,authenticationToken);
复制代码

Log in main flow

 public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info;
    try {
        /**
         * 取获校验token的信息
         * 如果有返回就认为登陆成功
         * 抛出任何AuthenticationException子类错误 就认为登陆失败
         */
        info = authenticate(token);
    } catch (AuthenticationException ae) {
        throw ae;
    }
    /**
     * 走到这里
     * 证明已经登陆成功
     * 
     * 下一步就是创建Subject
     */
    Subject loggedIn = createSubject(token, info, subject);
    return loggedIn;
}
复制代码

Log in identity verification

public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
    /**
     * 使用登陆器去登陆
     * 默认使用ModularRealmAuthenticator
     */
    return this.authenticator.authenticate(token);
}
复制代码
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
    assertRealmsConfigured();
    Collection<Realm> realms = getRealms();
    if (realms.size() == 1) {
        return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
    } else {
        //只演示单realm的情况
//            return doMultiRealmAuthentication(realms, authenticationToken);
        return null;
    }
}
复制代码
/**
     * 只有一个realm的情况下使用
     * @param realm
     * @param token
     * @return
     */
    protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
    /**
     * 先判断这个realm是否可以校验这个token
     * 这个方法需要我们在实现自己的realm时重写
     */
    if (!realm.supports(token)) {
        String msg = "Realm [" + realm + "] does not support authentication token [" +
                token + "].  Please ensure that the appropriate Realm implementation is " +
                "configured correctly or that the realm accepts AuthenticationTokens of this type.";
        throw new UnsupportedTokenException(msg);
    }
    /**
     * 从我们自己的realm中获取校验后的登陆信息
     */
    AuthenticationInfo info = realm.getAuthenticationInfo(token);
    if (info == null) {
        String msg = "Realm [" + realm + "] was unable to find account data for the " +
                "submitted AuthenticationToken [" + token + "].";
        throw new UnknownAccountException(msg);
    }
    return info;
}
复制代码

Creating Subject

 /**
 * 登陆成功后创建Subject
 * 如果已经有subject
 * 则把现在的认证信息与原来的Subject绑定
 * 如果没有Subject  则创建一个 并执行绑定操作
 * @param token
 * @param info
 * @param existing
 * @return
 */
protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {
    SubjectContext context = createSubjectContext();
    context.setAuthenticated(true);             //增加了登陆成功的标志
    /**
     * 同时保存了登陆的token和realm认证后返回的信息
     */
    context.setAuthenticationToken(token); 
    context.setAuthenticationInfo(info);
    if (existing != null) {
        context.setSubject(existing);
    }
    return createSubject(context);
}
复制代码
public Subject createSubject(SubjectContext subjectContext) {
    //复制了一遍subjectContext
    SubjectContext context = copy(subjectContext);
    //确保存在SecurityManager
    context = ensureSecurityManager(context);
    //解析Session
    context = resolveSession(context);
    Subject subject = doCreateSubject(context);
    /**
     * 保存subject
     * web情况下 会保存在session中
     */
    save(subject);
    return subject;
}
复制代码
protected Subject doCreateSubject(SubjectContext context) {
    //使用Subject工厂统一创建Subject
    return getSubjectFactory().createSubject(context);
}
复制代码
public Subject createSubject(SubjectContext context) {
    SecurityManager securityManager = context.resolveSecurityManager();
    //解析session
    Session session = context.resolveSession();
    /**
     * session是否自动创建标记
     * ,没开启 会报错 @DelegatingSubject$getSession(boolean create)
     */
    boolean sessionCreationEnabled = context.isSessionCreationEnabled();
    //realm中返回的用户凭证信息
    PrincipalCollection principals = context.resolvePrincipals();
    boolean authenticated = context.resolveAuthenticated();

    //创建
    return new DelegatingSubject(principals, authenticated, session, sessionCreationEnabled, securityManager);
}
复制代码

Save subject

/**
 * 具体保存逻辑
 * 由subjectDAO的实现类完成
 * @param subject
 */
protected void save(Subject subject) {
    this.subjectDAO.save(subject);
}
复制代码
/**
 * 默认的实现方式是把subject保存到session中
 * @param subject
 * @return
 */
public Subject save(Subject subject) {
    if (isSessionStorageEnabled(subject)) {
        saveToSession(subject);
    } else {
        log.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " +
                "authentication state are expected to be initialized on every request or invocation.", subject);
    }

    return subject;
}
复制代码
/**
 * 保存登陆的信息和登陆的状态
 * @param subject
 */
protected void saveToSession(Subject subject) {
    mergePrincipals(subject);
    mergeAuthenticationState(subject);
}
复制代码
protected void mergePrincipals(Subject subject) {
    PrincipalCollection currentPrincipals = null;
    if (currentPrincipals == null || currentPrincipals.isEmpty()) {
        currentPrincipals = subject.getPrincipals();
    }

    Session session = subject.getSession(false);

    /**
     * 如果有Session
     * 则把变动的PrincipalCollection保存进去
     */
    if (session == null) {
        if (!isEmpty(currentPrincipals)) {
            session = subject.getSession();
            session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);
        }
    } else {
        PrincipalCollection existingPrincipals =
                (PrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);

        if (isEmpty(currentPrincipals)) {
            if (!isEmpty(existingPrincipals)) {
                session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            }
        } else {
            //只有修改过了的,才会被保存
            if (!currentPrincipals.equals(existingPrincipals)) {
                session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);
            }
        }
    }
}
复制代码
/**
 * 刷新session中的登陆标记
 * @param subject
 */
protected void mergeAuthenticationState(Subject subject) {

    Session session = subject.getSession(false);

    if (session == null) {
        if (subject.isAuthenticated()) {
            /**
             * 如果登陆成功,并且第一次访问Session
             * 则会去创建也给新的
             */
            session = subject.getSession();
            session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);
        }
    } else {
        /**
         * 如果有session
         * 则更新标记
         */
        Boolean existingAuthc = (Boolean) session.getAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);

        if (subject.isAuthenticated()) {
            if (existingAuthc == null || !existingAuthc) {
                session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);
            }
        } else {
            if (existingAuthc != null) {
                session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
            }
        }
    }
}
复制代码

When you save the subject, how to generate the session (if enabled)

protected Session createSession(SessionContext context) throws AuthorizationException {
    return doCreateSession(context);
}
复制代码
protected Session doCreateSession(SessionContext context) {
    /**
     * 使用工厂类创建session
     * 默认SimpleSession
     */
    Session s = newSessionInstance(context);
    create(s);
    return s;
}
复制代码
public Session createSession(SessionContext initData) {
    /**
     * 这里生成sessionId 可以吗?
     */
    return new SimpleSession();
}
复制代码

shiro default MemorySessionDAO

protected void create(Session session) {
    if (log.isDebugEnabled()) {
        log.debug("Creating new EIS record for new session instance [" + session + "]");
    }
    //最终交由sessionDAO生成
    sessionDAO.create(session);
}
复制代码
protected Serializable doCreate(Session session) {
    /**
     * sessionId最终在SessionDAO中生成
     * 默认使用java的uuid
     */
    Serializable sessionId = generateSessionId(session);
    assignSessionId(session, sessionId);
    storeSession(sessionId, session);
    return sessionId;
}
复制代码

When modifying session, shiro is how to synchronize to a local (or HttpSession)

/**
 * 如果没有session
 * 则创建
 * @param create
 * @return
 */
@Override
public Session getSession(boolean create) {
    if (log.isTraceEnabled()) {
        log.trace("attempting to get session; create = " + create +
                "; session is null = " + (this.session == null) +
                "; session has id = " + (this.session != null && session.getId() != null));
    }

    if (this.session == null && create) {

        //added in 1.2:
        if (!isSessionCreationEnabled()) {      //没开启sessionCreationEnabled  会报错
            String msg = "Session creation has been disabled for the current subject.  This exception indicates " +
                    "that there is either a programming error (using a session when it should never be " +
                    "used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +
                    "for the current Subject.  See the " + DisabledSessionException.class.getName() + " JavaDoc " +
                    "for more.";
            throw new DisabledSessionException(msg);
        }

        /**
         * 创建的是DefaultSessionContext
         * 默认没什么内容
         */
        SessionContext sessionContext = createSessionContext();
        /**
         * 最终交由securityManager来生成Session
         */
        Session session = this.securityManager.start(sessionContext);
        /**
         * 最终让用户操作的多次包装的Session
         */
        this.session = decorate(session);
    }
    return this.session;
}
复制代码
 public Session start(SessionContext context) {
    /**.
     * 创建了一个SimpleSession
     */
    Session session = createSession(context);
    onStart(session, context);
    /**
     * 创建对外暴露的Session
     * 其实就是代理了一下
     *
     * 主要为了让session在普通的操作下完成更多的功能
     */
    return createExposedSession(session, context);
}
复制代码
protected Session createExposedSession(Session session, SessionContext context) {
    //代理Session
    return new DelegatingSession(this, new DefaultSessionKey(session.getId()));
}
复制代码
/**
 * 代理的session
 * 他会把对所有对session的操作
 * 提交给sessionManager去处理
 * @Author: lilingyan
 * @Date 2019/6/3 13:37
 */
public class DelegatingSession implements Session {

    private final SessionKey key;
    private final transient NativeSessionManager sessionManager;
    
    @Override
    public void setAttribute(Object attributeKey, Object value) throws InvalidSessionException {
        if (value == null) {
            removeAttribute(attributeKey);
        } else {
            //被代理了
            sessionManager.setAttribute(this.key, attributeKey, value);
        }
    }
    ...
复制代码
//在SessionManager中代理的处理方法
public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException {
    if (value == null) {
        removeAttribute(sessionKey, attributeKey);
    } else {
        Session s = lookupRequiredSession(sessionKey);
        s.setAttribute(attributeKey, value);
        onChange(s);
    }
}
...
复制代码
protected void onChange(Session session) {
    //最终任何修改都会被传递到sessionDAO
    sessionDAO.update(session);
}
复制代码

Reproduced in: https: //juejin.im/post/5cf4d92951882521bf340aa0

Guess you like

Origin blog.csdn.net/weixin_34405557/article/details/91469884