在 Shiro 中 SecurityUtils
是一个抽象类。并且没有任何子类。在其中声明了一个静态属性,三个静态方法。
静态属性 securityManager
private static SecurityManager securityManager;
用来存储当前应用中全局唯一的一个SecurityManager。
有两个静态方法是为此静态属性服务器,也就是下面这两个:
public static void setSecurityManager(SecurityManager securityManager) { SecurityUtils.securityManager = securityManager; } public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException { SecurityManager securityManager = ThreadContext.getSecurityManager(); if (securityManager == null) { securityManager = SecurityUtils.securityManager; } if (securityManager == null) { String msg = "No SecurityManager accessible to the calling code, either bound to the " + ThreadContext.class.getName() + " or as a vm static singleton. This is an invalid application " + "configuration."; throw new UnavailableSecurityManagerException(msg); } return securityManager; }
getSubject 静态方法
这个是 Shiro 中最核心的方法了,用来获取 Subject.
public static Subject getSubject() { Subject subject = ThreadContext.getSubject(); if (subject == null) { subject = (new Subject.Builder()).buildSubject(); ThreadContext.bind(subject); } return subject; }
上述方法中,第二行(Subject subject = ThreadContext.getSubject();
)获取到的Subject其实是第五行(ThreadContext.bind(subject);
)绑定的。
如果没有之前的绑定则得到null
,然后就会走第四行(subject = (new Subject.Builder()).buildSubject();
)获取。步骤如下:
- 调用Subject.Builder类的无参构造方法。如下代码:
public Builder() { this(SecurityUtils.getSecurityManager()); }
在这个无参构造方法中,以当前应用全局唯一的SecurityManager
对象为参调用了构造方法。如下:
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); }
其实这些都不重要,至此我们知道了 Subject.Builder
对象中的SecurityManager
对象,其实就是当前应用全局唯一的SecurityManager
对象。
注:以后在Shiro中,只要看到SecurityManager
对象,你就认为它是当前应用全局唯一的那个SecurityManager
对象就行了。
- 调用
Subject.Builder
对象的buildSubject
方法。
public Subject buildSubject() { return this.securityManager.createSubject(this.subjectContext); }
其实里面是调用了SecurityManager
对象的createSubject
方法的,至于那个subjectContext
参数,我们可以暂时不用理会。(在不同的应用环境下subjectContext
是不一样的,如Web环境下它默认是DefaultWebSubjectContext
)
// DefaultSecurityManager 中的 createSubject 方法 public Subject createSubject(SubjectContext subjectContext) { //create a copy so we don't modify the argument's backing map: SubjectContext context = copy(subjectContext); //ensure that the context has a SecurityManager instance, and if not, add one: context = ensureSecurityManager(context); //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before //sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the //process is often environment specific - better to shield the SF from these details: context = resolveSession(context); //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first //if possible before handing off to the SubjectFactory: context = resolvePrincipals(context); Subject subject = doCreateSubject(context); //save this subject for future reference if necessary: //(this is needed here in case rememberMe principals were resolved and they need to be stored in the //session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation). //Added in 1.2: save(subject); return subject; }
复制SubjectContext
对象之后执行的 context = ensureSecurityManager(context);
是为了确保在SubjectContext
对象中已经注入了当前应该用全局唯一的SecurityManager
对象:
// DefaultSecurityManager 中的 ensureSecurityManager 方法 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; }
也就是说,在SubjectContext
中的SecurityManager
也正是当前应该用全局唯一的SecurityManager
对象。
由createSubject
方法可知,在创建 Subject
前完成了以下两步工作:
- 解析了
Session
(context = resolveSession(context);
); - 解析了
Principals
(context = resolvePrincipals(context);
)。
创建Subject
之后,又执行了save(subject);
。如果继续扒代码你会发现,这一步其实是把Subject
存储到了Session
中。具体代码如下所示:
// DefaultSecurityManager 中的 save 方法 protected void save(Subject subject) { this.subjectDAO.save(subject); } // SubjectDAO 接口的默认实现类 DefaultSubjectDAO 中的 save 方法 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; } // SubjectDAO 接口的默认实现类 DefaultSubjectDAO 中的 saveToSession方法 protected void saveToSession(Subject subject) { //performs merge logic, only updating the Subject's session if it does not match the current state: mergePrincipals(subject); mergeAuthenticationState(subject); }
作者:JSON_NULL
链接:https://www.jianshu.com/p/cf95e3468638
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。