Subject

毫无疑问,Subject是Shiro最重要的一个概念。

“Subject”只是一个安全术语,意味着应用程序用户的特定于安全性的“视图”。Shiro Subject实例代表单个应用程序用户的安全状态和相关操作。

创建

初次创建是在AbstractShiroFilter#doFilterInternal方法中:

final Subject subject = createSubject(request, response);

protected WebSubject createSubject(ServletRequest request, ServletResponse response) {return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
}

创建的时候传入安全管理器,Subject.Builder是这样操作的:

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);
}public Subject buildSubject() {return this.securityManager.createSubject(this.subjectContext);
}

这个安全管理器还是我们指定的那个DefaultWebSecurityManager,一路传过去的。这个subjectContext参数是一个DefaultSubjectContext,子接口中的Builder覆盖了父类的方法,实际赋予的是一个DefaultWebSubjectContext。

protected Subject doCreateSubject(SubjectContext context) {return getSubjectFactory().createSubject(context);
}

subjectFactory是安全管理器DefaultWebSecurityManager中默认的DefaultWebSubjectFactory:

public DefaultWebSecurityManager() {super();((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());this.sessionMode = HTTP_SESSION_MODE;setSubjectFactory(new DefaultWebSubjectFactory());setRememberMeManager(new CookieRememberMeManager());setSessionManager(new ServletContainerSessionManager());
}

最终的创建又回到:

public class DefaultWebSubjectFactory extends DefaultSubjectFactory {public DefaultWebSubjectFactory() {super();}public Subject createSubject(SubjectContext context) {if (!(context instanceof WebSubjectContext)) {return super.createSubject(context);}WebSubjectContext wsc = (WebSubjectContext) context;SecurityManager securityManager = wsc.resolveSecurityManager();Session session = wsc.resolveSession();boolean sessionEnabled = wsc.isSessionCreationEnabled();PrincipalCollection principals = wsc.resolvePrincipals();boolean authenticated = wsc.resolveAuthenticated();String host = wsc.resolveHost();ServletRequest request = wsc.resolveServletRequest();ServletResponse response = wsc.resolveServletResponse();return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,request, response, securityManager);}//......
}

根据相关属性new出来一个WebDelegatingSubject。

shiro中很多都是这样的继承和组合关系:

DefaultSecurityManager -> DefaultSubjectFactory -> DelegatingSubject

DefaultWebSecurityManager -> DefaultWebSubjectFactory -> WebDelegatingSubject

再回到创建的方法:

final Subject subject = createSubject(request, response);//noinspection unchecked
subject.execute(new Callable() {public Object call() throws Exception {updateSessionLastAccessTime(request, response);executeChain(request, response, chain);return null;}
});

这个execute方法是在DelegatingSubject中实现的:

public <V> V execute(Callable<V> callable) throws ExecutionException {Callable<V> associated = associateWith(callable);try {return associated.call();} catch (Throwable t) {throw new ExecutionException(t);}
}public <V> Callable<V> associateWith(Callable<V> callable) {return new SubjectCallable<V>(this, callable);
}

SubjectCallable首先构造了一个ThreadState:

public SubjectCallable(Subject subject, Callable<V> delegate) {this(new SubjectThreadState(subject), delegate);
}

associated.call()调用ThreadState.bind():

public V call() throws Exception {try {threadState.bind();return doCall(this.callable);} finally {threadState.restore();}
}

SubjectThreadState的bind方法:

public void bind() {SecurityManager securityManager = this.securityManager;if ( securityManager == null ) {//try just in case the constructor didn't find one at the time:securityManager = ThreadContext.getSecurityManager();}this.originalResources = ThreadContext.getResources();ThreadContext.remove();ThreadContext.bind(this.subject);if (securityManager != null) {ThreadContext.bind(securityManager);}
}

这样就妥妥地把当前subject和线程绑定到了一起(还有securityManager)。

在这里遇到一个问题,记录下:
https://www.oschina.net/question/2275855_2273492

其实这只是每次进入核心过滤器时默认为我们创建的一个Subject,当调用subject.login方法之后会再次创建一个Subject,后面登录部分会做详细介绍。

获取

Subject subject = SecurityUtils.getSubject();

public static Subject getSubject() {Subject subject = ThreadContext.getSubject();if (subject == null) {subject = (new Subject.Builder()).buildSubject();ThreadContext.bind(subject);}return subject;
}

绑定是通过ThreadContext,获取当然也是从其取。

public abstract class ThreadContext {private static final Logger log = LoggerFactory.getLogger(ThreadContext.class);public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY";public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY";private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();private static Object getValue(Object key) {return resources.get().get(key);}public static Object get(Object key) {Object value = getValue(key);return value;}public static Subject getSubject() {return (Subject) get(SUBJECT_KEY);}}

最终是到ThreadLocal中拿,不过这个ThreadLocal是 InheritableThreadLocalMap 类型的(继承自InheritableThreadLocal)。

每个线程都有一个Map

SecurityManager

<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  <property name="realm" ref="casRealm" />  <property name="sessionManager" ref="sessionManager" /><property name="cacheManager" ref="shiroCacheManager" />  <!-- <property name="rememberMeManager" ref="rememberMeManager" />  -->
</bean>
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.ServletContainerSessionManager"/>
public interface SecurityManager extends Authenticator, Authorizer, SessionManager {Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;void logout(Subject subject);Subject createSubject(SubjectContext context);}

安全管理器继承了Authenticator, Authorizer, SessionManager三个接口。自顶向下第一个抽象类是CachingSecurityManager,接着是RealmSecurityManager,后面是AuthenticatingSecurityManager,AuthorizingSecurityManager,SessionsSecurityManager。

然后才是DefaultSecurityManager,DefaultWebSecurityManager。

由于是web项目,我们指定了DefaultWebSecurityManager,在构造器中会为我们设置相匹配的属性(都是和web相关的):

public DefaultWebSecurityManager() {super();((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());this.sessionMode = HTTP_SESSION_MODE;setSubjectFactory(new DefaultWebSubjectFactory());setRememberMeManager(new CookieRememberMeManager());setSessionManager(new ServletContainerSessionManager());
}

SessionsSecurityManager持有一个sessionManager对象,对sessionManager接口的实现是转移到这个对象上来的:

public abstract class SessionsSecurityManager extends AuthorizingSecurityManager {private SessionManager sessionManager;public SessionsSecurityManager() {super();this.sessionManager = new DefaultSessionManager();applyCacheManagerToSessionManager();}
}

AuthenticatingSecurityManager,AuthorizingSecurityManager类似,这两个抽象管理器在无参构造函数中创建了默认的对象:

public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager {private Authorizer authorizer;public AuthorizingSecurityManager() {super();this.authorizer = new ModularRealmAuthorizer();}}public abstract class AuthenticatingSecurityManager extends RealmSecurityManager {private Authenticator authenticator;public AuthenticatingSecurityManager() {super();this.authenticator = new ModularRealmAuthenticator();}
}

所以这个安全管理器几乎承担了所有的操作,然后转移到具体的对象。它的层次结构非常清晰,职责分明。对Subject所有操作最终都会转移到SecurityManager。

转载于:https://www.cnblogs.com/lucare/p/8679133.html

Shiro源码分析之Subject和SecurityManager相关推荐

  1. shiro源码分析(四)具体的Realm

    2019独角兽企业重金招聘Python工程师标准>>> 首先还是Realm的接口设计图: 这里只来说明SimpleAccountRealm和JdbcRealm的实现. 首先是Simp ...

  2. 源码分析shiro认证授权流程

    1. shiro介绍 Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户"登录": 授权 - ...

  3. shiro实现无状态的会话,带源码分析

    转载请在页首明显处注明作者与出处 朱小杰      http://www.cnblogs.com/zhuxiaojie/p/7809767.html 一:说明 在网上都找不到相关的信息,还是翻了大半天 ...

  4. Shiro源码学习之二

    接上一篇 Shiro源码学习之一 3.subject.login 进入login public void login(AuthenticationToken token) throws Authent ...

  5. Shiro源码学习之一

    一.最基本的使用 1.Maven依赖 <dependency><groupId>org.apache.shiro</groupId><artifactId&g ...

  6. java 线程池 源码_java线程池源码分析

    我们在关闭线程池的时候会使用shutdown()和shutdownNow(),那么问题来了: 这两个方法又什么区别呢? 他们背后的原理是什么呢? 线程池中线程超过了coresize后会怎么操作呢? 为 ...

  7. JDK动态代理实现原理详解(源码分析)

    无论是静态代理,还是Cglib动态代理,都比较容易理解,本文就通过进入源码的方式来看看JDK动态代理的实现原理进行分析 要了解动态代理的可以参考另一篇文章,有详细介绍,这里仅仅对JDK动态代理做源码分 ...

  8. Redis 数据结构-字典源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 相关文章 Redis 初探-安装与使用 Redis 数据结构-字符串源码分析 本文将从以下几个方面介绍 前言 字典结构图 字典 ...

  9. java观察者模式类图_设计模式(十八)——观察者模式(JDK Observable源码分析)...

    1 天气预报项目需求,具体要求以下: 1) 气象站能够将天天测量到的温度,湿度,气压等等以公告的形式发布出去(好比发布到本身的网站或第三方).java 2) 须要设计开放型 API,便于其余第三方也能 ...

最新文章

  1. 请问SAP PLM与WINDCHILL比优势在哪里?
  2. SAP MM Purchasing Report中Selection Parameter WE101 WE103
  3. 悬镜 linux防黑加固平台,悬镜答疑丨悬镜服务器防护CC效果如何?
  4. 使用TWebBrowser组件保存网页为html和mht文件 收藏
  5. 时间序列研(part7)--单位根检验
  6. 导出配置_Lua配置表导出优化
  7. swiper动态加载数据滑动失效,ajax执行后swiper.js的效果消失问题
  8. 机器学习-吴恩达-笔记-3-正则化
  9. vue调用const_2020年Vue的这些面试题你会吗?
  10. Spring限定注入逻辑分组@Qualifier
  11. 剑指 Offer 06. 从尾到头打印链表-力扣
  12. Linux下Intel网卡固件烧写工具
  13. Flash制作标题出现动画
  14. 淘宝API接口 item_search - 按关键字搜索淘宝商品
  15. 周杰伦要出新专辑了?上 Instagram 看看
  16. python第二版课后答案第七章7.5_IDA7.5 启动基础配置
  17. 设置Chrome浏览器不加载图片的方法
  18. 写一个函数,将一个字符串中的元音字母复制到另一字符串,然后输出。
  19. java练习04|银行利率表如下表所示,请计算存款10000元,活期1年、活期2年,定期1年,定期2年后的本息合计。
  20. excel快捷键附录笔记

热门文章

  1. Python爬虫_HTTP标准
  2. 用jQuery实现返回页面顶部的功能
  3. 计算机网络「五」 运输层
  4. CWnd类与Windows窗口的关系-3、CWnd类如何封装Windows窗口
  5. 特征点提取—尺度不变特征SIFT算法
  6. 7-135 二叉搜索树的2层结点统计
  7. 7-19 谁先倒 (15 分)
  8. 4014-基于邻接表的长度为k的简单路径的求解(C++,附思路)
  9. 哈夫曼树(利用python实现)
  10. hive中groupby优化_Hive数据倾斜