学习 准备 尝试 谨慎小心

0%

shiro源码解析之Subject和SubjectContext

解析Subject

了解 Subject

继承图

WebDelegatingSubject继承图

Subject

Subject方法属性

Subject内部类Builder的方法属性

DelegatingSubject

DelegatingSubject方法属性

WebDelegatingSubject

WebDelegatingSubject方法属性

分析 Subject

在登录流程中,我们经常用到的方法有 getPrincipal()getSession()login()logout

getPrincipal(): Object

该方法在 DelegatingSubject 实现

1
private Object getPrimaryPrincipal(PrincipalCollection principals) {
2
    if (!isEmpty(principals)) {
3
        return principals.getPrimaryPrincipal();
4
    }
5
    return null;
6
}
7
8
/**
9
 * @see Subject#getPrincipal()
10
 */
11
public Object getPrincipal() {
12
    return getPrimaryPrincipal(getPrincipals());
13
}
14
15
public PrincipalCollection getPrincipals() {
16
    List<PrincipalCollection> runAsPrincipals = getRunAsPrincipalsStack();
17
    return CollectionUtils.isEmpty(runAsPrincipals) ? this.principals : runAsPrincipals.get(0);
18
}
19
......
20
// line 169
21
private List<PrincipalCollection> getRunAsPrincipalsStack() {
22
    Session session = getSession(false);
23
    if (session != null) {
24
        return (List<PrincipalCollection>) session.getAttribute(RUN_AS_PRINCIPALS_SESSION_KEY);
25
    }
26
    return null;
27
}

getSession() : Session

该方法在 DelegatingSubject 中实现的

1
// line 314
2
public Session getSession() {
3
    return getSession(true);
4
}
5
6
public Session getSession(boolean create) {
7
    if (log.isTraceEnabled()) {
8
        log.trace("attempting to get session; create = " + create +
9
                "; session is null = " + (this.session == null) +
10
                "; session has id = " + (this.session != null && session.getId() != null));
11
    }
12
13
    if (this.session == null && create) {
14
15
        //added in 1.2:
16
        if (!isSessionCreationEnabled()) {
17
            String msg = "Session creation has been disabled for the current subject.  This exception indicates " +
18
                    "that there is either a programming error (using a session when it should never be " +
19
                    "used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +
20
                    "for the current Subject.  See the " + DisabledSessionException.class.getName() + " JavaDoc " +
21
                    "for more.";
22
            throw new DisabledSessionException(msg);
23
        }
24
25
        log.trace("Starting session for host {}", getHost());
26
        SessionContext sessionContext = createSessionContext();
27
        Session session = this.securityManager.start(sessionContext);
28
        this.session = decorate(session);
29
    }
30
    return this.session;
31
}
32
33
protected SessionContext createSessionContext() {
34
    SessionContext sessionContext = new DefaultSessionContext();
35
    if (StringUtils.hasText(host)) {
36
        sessionContext.setHost(host);
37
    }
38
    return sessionContext;
39
}

getSession() 等同于 getSession(true) ,如果 session 不存在,则创建一个

解析SubjectContext

了解SubjectContext

继承图

DefaultSubjectContext继承图

SubjectContext

SubjectContext属性方法

MapContext

MapContext属性方法

DefaultSubjectContext

DefaultSubjectContext属性方法

DefaultWebSubjectContext

DefaultWebSubjectContext属性方法

小结

MapContext 的Map对象 backingMap 专门用来保存各种类型的变量,看它的定义就可明白

1
private final Map<String, Object> backingMap;
2
3
public MapContext() {
4
    this.backingMap = new HashMap<String, Object>();
5
}

而 DefaultSubjectContext 和 DefaultWebSubjectContext 继承了这一点,并在此基础上添加了多个 setter 和 getter ,用来向map添加元素,以及从map中获取元素。

但是要注意名称如 resolveXxx 这样的方法。

理解 SubjectContext

setXxx 方法

1
public void setSecurityManager(SecurityManager securityManager) {
2
    nullSafePut(SECURITY_MANAGER, securityManager);
3
}

nullSafePut(String, Object) 方法继承自 MapContext

1
// line 73
2
/**
3
 * Places a value in this context map under the given key only if the given {@code value} argument is not null.
4
 *
5
 * @param key   the attribute key under which the non-null value will be stored
6
 * @param value the non-null value to store.  If {@code null}, this method does nothing and returns immediately.
7
 */
8
protected void nullSafePut(String key, Object value) {
9
    if (value != null) {
10
        put(key, value);
11
    }
12
}
13
......
14
// line 105
15
public Object put(String s, Object o) {
16
    return backingMap.put(s, o);
17
}

setXxx 方法就是将 Xxx 添加到 backingMap 中

getXxx 方法

1
public SecurityManager getSecurityManager() {
2
    return getTypedValue(SECURITY_MANAGER, SecurityManager.class);
3
}

getTypedValue 同样继承自 MapContext

1
// line 48
2
/**
3
 * Performs a {@link #get get} operation but additionally ensures that the value returned is of the specified
4
 * {@code type}.  If there is no value, {@code null} is returned.
5
 *
6
 * @param key  the attribute key to look up a value
7
 * @param type the expected type of the value
8
 * @param <E>  the expected type of the value
9
 * @return the typed value or {@code null} if the attribute does not exist.
10
 */
11
@SuppressWarnings({"unchecked"})
12
protected <E> E getTypedValue(String key, Class<E> type) {
13
    E found = null;
14
    Object o = backingMap.get(key);
15
    if (o != null) {
16
        if (!type.isAssignableFrom(o.getClass())) {
17
            String msg = "Invalid object found in SubjectContext Map under key [" + key + "].  Expected type " +
18
                    "was [" + type.getName() + "], but the object under that key is of type " +
19
                    "[" + o.getClass().getName() + "].";
20
            throw new IllegalArgumentException(msg);
21
        }
22
        found = (E) o;
23
    }
24
    return found;
25
}

getXxx 方法中调用 getTypedValue(String, Class) 。在该方法中,根据 key 从 backingMap 中获取值,然后判断该值是否是 Class 对象。

resolveXxx 方法

resolveSecurityManager()

1
// line 96
2
public SecurityManager resolveSecurityManager() {
3
    SecurityManager securityManager = getSecurityManager();
4
    if (securityManager == null) {
5
        if (log.isDebugEnabled()) {
6
            log.debug("No SecurityManager available in subject context map.  " +
7
                    "Falling back to SecurityUtils.getSecurityManager() lookup.");
8
        }
9
        try {
10
            securityManager = SecurityUtils.getSecurityManager();
11
        } catch (UnavailableSecurityManagerException e) {
12
            if (log.isDebugEnabled()) {
13
                log.debug("No SecurityManager available via SecurityUtils.  Heuristics exhausted.", e);
14
            }
15
        }
16
    }
17
    return securityManager;
18
}

先调用 getSecurityManager() 从 backingMap 中获取对象;

为空的话,则调用 SecurityUtils.getSecurityManager()

resolveSession()

1
public Session resolveSession() {
2
    Session session = getSession();
3
    if (session == null) {
4
        //try the Subject if it exists:
5
        Subject existingSubject = getSubject();
6
        if (existingSubject != null) {
7
            session = existingSubject.getSession(false);
8
        }
9
    }
10
    return session;
11
}
  • 先调用 getSession() 从 backingMap 中获取对象;
  • 为空的话,则去 subject 中获取

resolvePrincipals()

1
public PrincipalCollection resolvePrincipals() {
2
        PrincipalCollection principals = getPrincipals();
3
4
        if (isEmpty(principals)) {
5
            //check to see if they were just authenticated:
6
            AuthenticationInfo info = getAuthenticationInfo();
7
            if (info != null) {
8
                principals = info.getPrincipals();
9
            }
10
        }
11
12
        if (isEmpty(principals)) {
13
            Subject subject = getSubject();
14
            if (subject != null) {
15
                principals = subject.getPrincipals();
16
            }
17
        }
18
19
        if (isEmpty(principals)) {
20
            //try the session:
21
            Session session = resolveSession();
22
            if (session != null) {
23
                principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);
24
            }
25
        }
26
27
        return principals;
28
    }
  • 先调用 getPrincipals() 从 backingMap 中获取对象;
  • 为空的话,则去AuthenticationInfo对象中获取;
  • 还为空的话,则去Subject对象中获取;
  • 还为空的话,则去Session对象中获取。