解析Subject
了解 Subject
继承图

Subject


DelegatingSubject

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
继承图

SubjectContext

MapContext

DefaultSubjectContext

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 | ({"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对象中获取。