外贸app英文seo外链
前言
本文属于源码分析,只想了解Shiro使用的读者请略过。
Subject接口是Shiro中非常重要的接口,客户端通常会使用Subject的方法进行登录、登出、鉴权、角色判断等功能。在《Shiro学习(一)——Shiro配置与快速开始》中,我们就使用了其login接口。Subject的功能似乎非常强大,那么它到底是什么,为什么能做这么多事呢,值得我们分析一下它的来龙去脉。
Subject创建
在《Shiro学习(一)——Shiro配置与快速开始》为例,Subject通过SecurityUtils.getSubject()方法获取,如下:
Subject subject = SecurityUtils.getSubject();
深入到SecurityUtils的源码中看看:
public abstract class SecurityUtils {//......public static Subject getSubject() {Subject subject = ThreadContext.getSubject();if (subject == null) {subject = (new Subject.Builder()).buildSubject();ThreadContext.bind(subject);}return subject;}//......
}
ThreadContext是shiro封装的一个操作线程局部变量的抽象类。第6行通过getSubject()方法获取Subject。在测试的例子中,因为还没创建Subject,所以第7行判断为null,因此第8行创建subject。看看Subject.Builder()干了什么:
public interface Subject {//......public static class Builder {private final SubjectContext subjectContext;private final SecurityManager securityManager;//......public Builder() {this(SecurityUtils.getSecurityManager());}public Builder(SecurityManager securityManager) {if (securityManager == null) {throw new NullPointerException("SecurityManager method argument cannot be null.");}this.securityManager = securityManager;this.subjectContext = newSubjectContextInstance();if (this.subjectContext == null) {throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +"cannot be null.");}this.subjectContext.setSecurityManager(securityManager);}protected SubjectContext newSubjectContextInstance() {return new DefaultSubjectContext();}
//......public Subject buildSubject() {return this.securityManager.createSubject(this.subjectContext);}}
}
初始化Builder主要做两件事,一是设置SecurityManager,二是创建DefaultSubjectContext作为SubjectContext。接着看看(new Subject.Builder()).buildSubject()怎么创建Subject。它调用了33-35行,34行返回一个由securityManager创建的subejct。因此我们要跟踪securityManager的createSubject方法,前面都是创建subject的外围,接下来才是创建subject对象的核心:
public class DefaultSecurityManager extends SessionsSecurityManager {//......protected SubjectContext copy(SubjectContext subjectContext) {return new DefaultSubjectContext(subjectContext);}public Subject createSubject(SubjectContext subjectContext) {SubjectContext context = copy(subjectContext);context = ensureSecurityManager(context);context = resolveSession(context);context = resolvePrincipals(context);Subject subject = doCreateSubject(context);save(subject);return subject;}protected Subject doCreateSubject(SubjectContext context) {return getSubjectFactory().createSubject(context);}protected SubjectContext ensureSecurityManager(SubjectContext context) {if (context.resolveSecurityManager() != null) {log.trace("Context already contains a SecurityManager instance. Returning.");return context;}log.trace("No SecurityManager found in context. Adding self reference.");context.setSecurityManager(this);return context;}protected SubjectContext resolveSession(SubjectContext context) {if (context.resolveSession() != null) {log.debug("Context already contains a session. Returning.");return context;}try {Session session = resolveContextSession(context);if (session != null) {context.setSession(session);}} catch (InvalidSessionException e) {log.debug("Resolved SubjectContext context session is invalid. Ignoring and creating an anonymous " +"(session-less) Subject instance.", e);}return context;}protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException {SessionKey key = getSessionKey(context);if (key != null) {return getSession(key);}return null;}protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException {SessionKey key = getSessionKey(context);if (key != null) {return getSession(key);}return null;}protected SessionKey getSessionKey(SubjectContext context) {Serializable sessionId = context.getSessionId();if (sessionId != null) {return new DefaultSessionKey(sessionId);}return null;}protected SubjectContext resolvePrincipals(SubjectContext context) {PrincipalCollection principals = context.resolvePrincipals();if (isEmpty(principals)) {log.trace("No identity (PrincipalCollection) found in the context. Looking for a remembered identity.");principals = getRememberedIdentity(context);if (!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;}//......
}
创建subejct第一件工作是创建SubjectContext,就是subject上下文。第10行复制一份刚才Builder创建的SubjectContext。当然这种复制对subjectContext内部的Map中的对象是浅拷贝,这样使每一个复制出来的SubjectContext都包含了设置好的SecurityManager。12行确保SecurityManager已被设置到SubjectContext中。13行设置Session到SubjectContext中,37行判断SubjectContext是否已有Session,因为SubjectContext从Builder复制出来只包含了SecurityManager,所以是null,因此走43行。由于之前SubjectContext没有设置过Session,因此43行也是返回null。14行是处理Principal,因为之前创建的SubjectContext没有涉及Principal,因此14行是指也没有做任何操作。16行创建Subject,跳转到23行调用DefaultSubjectFactory的createSubject方法。跟踪DefaultSubjectFactory的createSubject方法:
public class DefaultSubjectFactory implements SubjectFactory {//......public Subject createSubject(SubjectContext context) {SecurityManager securityManager = context.resolveSecurityManager();Session session = context.resolveSession();boolean sessionCreationEnabled = context.isSessionCreationEnabled();PrincipalCollection principals = context.resolvePrincipals();boolean authenticated = context.resolveAuthenticated();String host = context.resolveHost();return new DelegatingSubject(principals, authenticated, host, session, sessionCreationEnabled, securityManager);}//......
}
这里会把session、principals、authenticated、host、sessionCreationEnabled查出,并设置到DelegatingSubject对象中。因此Subject subject = SecurityUtils.getSubject();这个语句最终返回一个DelegatingSubject对象。对于本文的例子而言,session、principals、host均为null,sessionCreationEnabled为true,authenticated为false。