Shiro 中的 SecurityUtils(转)

🏷️ det365手机版 📅 2026-02-06 02:59:23 ✍️ admin 👀 7231 ❤️ 193
Shiro 中的 SecurityUtils(转)

在 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);

}

🎯 相关推荐

CorelDRAW对齐对象的快捷操作
365bet现金信誉网

CorelDRAW对齐对象的快捷操作

📅 09-09 👀 7771
苗木扦插育苗技术要点(扦插树苗的方法和技术)
爱奇艺怎么开通会员
det365手机版

爱奇艺怎么开通会员

📅 09-13 👀 2011