学习 准备 尝试 谨慎小心

0%

shiro源码解析之SecurityUtils.getSubject()

SecurityUtils.getSubject()

1
// line 38
2
/**
3
 * Returns the currently accessible {@code Subject} available to the calling code depending on
4
 * runtime environment.
5
 * <p/>
6
 * This method is provided as a way of obtaining a {@code Subject} without having to resort to
7
 * implementation-specific methods.  It also allows the Shiro team to change the underlying implementation of
8
 * this method in the future depending on requirements/updates without affecting your code that uses it.
9
 *
10
 * @return the currently accessible {@code Subject} accessible to the calling code.
11
 * @throws IllegalStateException if no {@link Subject Subject} instance or
12
 *                               {@link SecurityManager SecurityManager} instance is available with which to obtain
13
 *                               a {@code Subject}, which which is considered an invalid application configuration
14
 *                               - a Subject should <em>always</em> be available to the caller.
15
 */
16
public static Subject getSubject() {
17
    Subject subject = ThreadContext.getSubject();
18
    if (subject == null) {
19
        subject = (new Subject.Builder()).buildSubject();
20
        ThreadContext.bind(subject);
21
    }
22
    return subject;
23
}

ThreadContext.getSubject()

从线程局部变量 ThreadLocal<Map<String, Object>> resource 里,获取 key 为 ThreadContext.class.getName() + "_SUBJECT_KEY" 的 Subject 对象

1
/**
2
 * Convenience method that simplifies retrieval of a thread-bound Subject.  If there is no
3
 * Subject bound to the thread, this method returns <tt>null</tt>.  It is merely a convenient wrapper
4
 * for the following:
5
 * <p/>
6
 * <code>return (Subject)get( SUBJECT_KEY );</code>
7
 * <p/>
8
 * This method only returns the bound value if it exists - it does not remove it
9
 * from the thread.  To remove it, one must call {@link #unbindSubject() unbindSubject()} instead.
10
 *
11
 * @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound.
12
 * @since 0.2
13
 */
14
public static Subject getSubject() {
15
	// public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY";
16
    return (Subject) get(SUBJECT_KEY);
17
}
18
19
20
 /**
21
 * Returns the object for the specified <code>key</code> that is bound to
22
 * the current thread.
23
 *
24
 * @param key the key that identifies the value to return
25
 * @return the object keyed by <code>key</code> or <code>null</code> if
26
 *         no value exists for the specified <code>key</code>
27
 */
28
public static Object get(Object key) {
29
    if (log.isTraceEnabled()) {
30
        String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
31
        log.trace(msg);
32
    }
33
34
    Object value = getValue(key);
35
    if ((value != null) && log.isTraceEnabled()) {
36
        String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
37
                key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
38
        log.trace(msg);
39
    }
40
    return value;
41
}
42
43
44
/**
45
 * Returns the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there
46
 * is no value for that {@code key}.
47
 *
48
 * @param key the map key to use to lookup the value
49
 * @return the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there
50
 *         is no value for that {@code key}.
51
 * @since 1.0
52
 */
53
private static Object getValue(Object key) {
54
	// private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
55
    Map<Object, Object> perThreadResources = resources.get();
56
    return perThreadResources != null ? perThreadResources.get(key) : null;
57
}

(new Subject.Builder()).buildSubject()

(new Subject.Builder())

这里暂时不去穷究 SecurityUtils.getSecurityManager()

1
/**
2
 * Constructs a new {@link Subject.Builder} instance, using the {@code SecurityManager} instance available
3
 * to the calling code as determined by a call to {@link org.apache.shiro.SecurityUtils#getSecurityManager()}
4
 * to build the {@code Subject} instance.
5
 */
6
public Builder() {
7
    this(SecurityUtils.getSecurityManager());
8
}
9
10
/**
11
 * Constructs a new {@link Subject.Builder} instance which will use the specified {@code SecurityManager} when
12
 * building the {@code Subject} instance.
13
 *
14
 * @param securityManager the {@code SecurityManager} to use when building the {@code Subject} instance.
15
 */
16
public Builder(SecurityManager securityManager) {
17
    if (securityManager == null) {
18
        throw new NullPointerException("SecurityManager method argument cannot be null.");
19
    }
20
    this.securityManager = securityManager;
21
    this.subjectContext = newSubjectContextInstance();
22
    if (this.subjectContext == null) {
23
        throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
24
                "cannot be null.");
25
    }
26
    this.subjectContext.setSecurityManager(securityManager);
27
}

(new Subject.Builder()).buildSubject()

1
/**
2
 * Creates and returns a new {@code Subject} instance reflecting the cumulative state acquired by the
3
 * other methods in this class.
4
 * <p/>
5
 * This {@code Builder} instance will still retain the underlying state after this method is called - it
6
 * will not clear it; repeated calls to this method will return multiple {@link Subject} instances, all
7
 * reflecting the exact same state.  If a new (different) {@code Subject} is to be constructed, a new
8
 * {@code Builder} instance must be created.
9
 * <p/>
10
 * <b>Note</b> that the returned {@code Subject} instance is <b>not</b> automatically bound to the application
11
 * (thread) for further use.  That is,
12
 * {@link org.apache.shiro.SecurityUtils SecurityUtils}.{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()}
13
 * will not automatically return the same instance as what is returned by the builder.  It is up to the
14
 * framework developer to bind the returned {@code Subject} for continued use if desired.
15
 *
16
 * @return a new {@code Subject} instance reflecting the cumulative state acquired by the
17
 *         other methods in this class.
18
 */
19
public Subject buildSubject() {
20
    return this.securityManager.createSubject(this.subjectContext);
21
}
22
23
// 在构造函数中 `this.subjectContext = newSubjectContextInstance();`
24
/**
25
 * Creates a new {@code SubjectContext} instance to be used to populate with subject contextual data that
26
 * will then be sent to the {@code SecurityManager} to create a new {@code Subject} instance.
27
 *
28
 * @return a new {@code SubjectContext} instance
29
 */
30
protected SubjectContext newSubjectContextInstance() {
31
    return new DefaultSubjectContext();
32
}

ThreadContext.bind(subject);

把 subject 绑定到线程内部

1
/**
2
 * Convenience method that simplifies binding a Subject to the ThreadContext.
3
 * <p/>
4
 * <p>The method's existence is to help reduce casting in your own code and to simplify remembering of
5
 * ThreadContext key names.  The implementation is simple in that, if the Subject is not <tt>null</tt>,
6
 * it binds it to the thread, i.e.:
7
 * <p/>
8
 * <pre>
9
 * if (subject != null) {
10
 *     put( SUBJECT_KEY, subject );
11
 * }</pre>
12
 *
13
 * @param subject the Subject object to bind to the thread.  If the argument is null, nothing will be done.
14
 * @since 0.2
15
 */
16
public static void bind(Subject subject) {
17
    if (subject != null) {
18
        put(SUBJECT_KEY, subject);
19
    }
20
}

小结

SecurityUtils.getSubject()ThreadContext.getSubject() 来获取subject;如果为null,则调用 (new Subject.Builder()).buildSubject() 来创建subject,在该方法内部最终调用 SecurityManager.createSubject(subjectContext)