在 Shiro 中 SecurityUtils 是一个抽象类。并且没有任何子类。在其中声明了一个静态属性,三个静态方法。

静态属性 securityManager

  1. private static SecurityManager securityManager;

用来存储当前应用中全局唯一的一个SecurityManager。

有两个静态方法是为此静态属性服务器,也就是下面这两个:

  1. public static void setSecurityManager(SecurityManager securityManager) {
  2. SecurityUtils.securityManager = securityManager;
  3. }
  4. public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException {
  5. SecurityManager securityManager = ThreadContext.getSecurityManager();
  6. if (securityManager == null) {
  7. securityManager = SecurityUtils.securityManager;
  8. }
  9. if (securityManager == null) {
  10. String msg = "No SecurityManager accessible to the calling code, either bound to the " +
  11. ThreadContext.class.getName() + " or as a vm static singleton. This is an invalid application " +
  12. "configuration.";
  13. throw new UnavailableSecurityManagerException(msg);
  14. }
  15. return securityManager;
  16. }

getSubject 静态方法

这个是 Shiro 中最核心的方法了,用来获取 Subject.

  1. public static Subject getSubject() {
  2. Subject subject = ThreadContext.getSubject();
  3. if (subject == null) {
  4. subject = (new Subject.Builder()).buildSubject();
  5. ThreadContext.bind(subject);
  6. }
  7. return subject;
  8. }

上述方法中,第二行(Subject subject = ThreadContext.getSubject();)获取到的Subject其实是第五行(ThreadContext.bind(subject);)绑定的。

如果没有之前的绑定则得到null,然后就会走第四行(subject = (new Subject.Builder()).buildSubject();)获取。步骤如下:

  1. 调用Subject.Builder类的无参构造方法。如下代码:
  1. public Builder() {
  2. this(SecurityUtils.getSecurityManager());
  3. }

在这个无参构造方法中,以当前应用全局唯一的SecurityManager对象为参调用了构造方法。如下:

  1. public Builder(SecurityManager securityManager) {
  2. if (securityManager == null) {
  3. throw new NullPointerException("SecurityManager method argument cannot be null.");
  4. }
  5. this.securityManager = securityManager;
  6. this.subjectContext = newSubjectContextInstance();
  7. if (this.subjectContext == null) {
  8. throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
  9. "cannot be null.");
  10. }
  11. this.subjectContext.setSecurityManager(securityManager);
  12. }

其实这些都不重要,至此我们知道了 Subject.Builder 对象中的SecurityManager对象,其实就是当前应用全局唯一的SecurityManager对象。

注:以后在Shiro中,只要看到SecurityManager对象,你就认为它是当前应用全局唯一的那个SecurityManager对象就行了。

  1. 调用 Subject.Builder 对象的buildSubject方法。
  1. public Subject buildSubject() {
  2. return this.securityManager.createSubject(this.subjectContext);
  3. }

其实里面是调用了SecurityManager对象的createSubject方法的,至于那个subjectContext参数,我们可以暂时不用理会。(在不同的应用环境下subjectContext是不一样的,如Web环境下它默认是DefaultWebSubjectContext

  1. // DefaultSecurityManager 中的 createSubject 方法
  2. public Subject createSubject(SubjectContext subjectContext) {
  3. //create a copy so we don't modify the argument's backing map:
  4. SubjectContext context = copy(subjectContext);
  5. //ensure that the context has a SecurityManager instance, and if not, add one:
  6. context = ensureSecurityManager(context);
  7. //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before
  8. //sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the
  9. //process is often environment specific - better to shield the SF from these details:
  10. context = resolveSession(context);
  11. //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first
  12. //if possible before handing off to the SubjectFactory:
  13. context = resolvePrincipals(context);
  14. Subject subject = doCreateSubject(context);
  15. //save this subject for future reference if necessary:
  16. //(this is needed here in case rememberMe principals were resolved and they need to be stored in the
  17. //session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation).
  18. //Added in 1.2:
  19. save(subject);
  20. return subject;
  21. }

复制SubjectContext 对象之后执行的 context = ensureSecurityManager(context);是为了确保在SubjectContext对象中已经注入了当前应该用全局唯一的SecurityManager对象:

  1. // DefaultSecurityManager 中的 ensureSecurityManager 方法
  2. protected SubjectContext ensureSecurityManager(SubjectContext context) {
  3. if (context.resolveSecurityManager() != null) {
  4. log.trace("Context already contains a SecurityManager instance. Returning.");
  5. return context;
  6. }
  7. log.trace("No SecurityManager found in context. Adding self reference.");
  8. context.setSecurityManager(this);
  9. return context;
  10. }

也就是说,在SubjectContext中的SecurityManager也正是当前应该用全局唯一的SecurityManager对象。

createSubject方法可知,在创建 Subject 前完成了以下两步工作:

  1. 解析了Sessioncontext = resolveSession(context););
  2. 解析了Principalscontext = resolvePrincipals(context);)。

创建Subject之后,又执行了save(subject);。如果继续扒代码你会发现,这一步其实是把Subject存储到了Session中。具体代码如下所示:

  1. // DefaultSecurityManager 中的 save 方法
  2. protected void save(Subject subject) {
  3. this.subjectDAO.save(subject);
  4. }
  5. // SubjectDAO 接口的默认实现类 DefaultSubjectDAO 中的 save 方法
  6. public Subject save(Subject subject) {
  7. if (isSessionStorageEnabled(subject)) {
  8. saveToSession(subject);
  9. } else {
  10. log.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " +
  11. "authentication state are expected to be initialized on every request or invocation.", subject);
  12. }
  13. return subject;
  14. }
  15. // SubjectDAO 接口的默认实现类 DefaultSubjectDAO 中的 saveToSession方法
  16. protected void saveToSession(Subject subject) {
  17. //performs merge logic, only updating the Subject's session if it does not match the current state:
  18. mergePrincipals(subject);
  19. mergeAuthenticationState(subject);
  20. }

作者:JSON_NULL
链接:https://www.jianshu.com/p/cf95e3468638
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Shiro 中的 SecurityUtils(转)的更多相关文章

  1. Shiro中的授权问题(二)

    上篇博客(Shiro中的授权问题 )我们介绍了Shiro中最最基本的授权问题,以及常见的权限字符的匹配问题.但是这里边还有许多细节需要我们继续介绍,本节我们就来看看Shiro中授权的一些细节问题. 验 ...

  2. Shiro中的授权问题

    在初识Shiro一文中,我们对Shiro的基本使用已经做了简单的介绍,不懂的小伙伴们可以先阅读上文,今天我们就来看看Shiro中的授权问题. Shiro中的授权,大体上可以分为两大类,一类是隐式角色, ...

  3. Shiro中的Rememberme后出现浏览器500错误

    问题详述:在Shiro中添加Remember me功能后,只要勾选Remember me选项为true的时候,浏览器就会跳转到一个不可达页面,并且在Chrome中显示HTTP 500错误. 问题追踪: ...

  4. Shiro中Realm

    6.1 Realm [2.5 Realm]及[3.5 Authorizer]部分都已经详细介绍过Realm了,接下来再来看一下一般真实环境下的Realm如何实现. 1.定义实体及关系   即用户-角色 ...

  5. shiro中INI配置

    4.1 根对象SecurityManager 从之前的Shiro架构图可以看出,Shiro是从根对象SecurityManager进行身份验证和授权的:也就是所有操作都是自它开始的,这个对象是线程安全 ...

  6. 项目一:第十三天 1、菜单数据管理 2、权限数据管理 3、角色数据管理 4、用户数据管理 5、在realm中动态查询用户权限,角色 6、Shiro中整合ehcache缓存权限数据

    1 课程计划 菜单数据管理 权限数据管理 角色数据管理 用户数据管理 在realm中动态查询用户权限,角色 Shiro中整合ehcache缓存权限数据         2 菜单数据添加 2.1 使用c ...

  7. shiro中接入单点登录功能

    最近新建的系统中使用了shiro,而shiro框架中包含登录认证和鉴权的功能,因为我们系统要统一接入公司内部的单点登录(isso)系统,所以通过isso的登录用户,需要在shiro中置为已认证,一下提 ...

  8. Shiro中Subject对象的创建与绑定流程分析

    我们在平常使用Shrio进行身份认证时,经常通过获取Subject 对象中保存的Session.Principal等信息,来获取认证用户的信息,也就是说Shiro会把认证后的用户信息保存在Subjec ...

  9. 从零到实现Shiro中Authorization和Authentication的缓存

    本文大纲 一.简介 二.缓存的概念 三.自定义实现缓存机制 四.什么是Ehcache 五.Ehcache怎么用 六.Spring对缓存的支持 七.Spring+Ehcache实现 八.Spring+S ...

随机推荐

  1. [转帖]Linux 中的零拷贝技术,第 1 部分

    Linux 中的零拷贝技术,第 1 部分 https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy1/index.html   引言 传统的 ...

  2. python 如何让字符串的不具有转义的反斜杠具有转义功能

    用 codecs, 大概是這樣子: # python3 code import codecs print(codecs.getdecoder("unicode_escape")(' ...

  3. 剑指offer52:正则表达式匹配

    1 题目描述 请实现一个函数用来匹配包括'.'和'*'的正则表达式.模式中的字符‘.’表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次). 在本题中,匹配是指字符串的所有字符匹配整个 ...

  4. mysql中char和varchar区别

    char是一种固定长度的类型,varchar则是一种可变长度的类型  char(M)类型的数据列里,每个值都占用M个字节,如果某个长度小于M,MySQL就会在它的右边用空格字符补足.(在检索操作中那些 ...

  5. QT聊天室--重大bug

    发送qqqqqqqqqqqqqqqqqqqqqqq: 发送test

  6. C# DateTime Subtract

    DateTime start = DateTime.Now.AddDays(-20); DateTime end = DateTime.Now; TimeSpan ts = end.Subtract( ...

  7. Bootstrap3 CDN 使用手册

    一.一般功能 <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel=" ...

  8. python练习:函数2

    习题: 定义一个方法get_num(num),num参数是列表类型,判断列表里面的元素为数字类型.其他类型则报错,并且返回一个偶数列表:(注:列表里面的元素为偶数). def get_num(num) ...

  9. SQL Server 2017 左补齐

    DECLARE @NUM CHAR(3)='7  'SELECT RIGHT('0000000'+CONVERT(VARCHAR(50),1+ RTRIM(@NUM)),7) ​​​​

  10. 空a标签在IE下无效之解决方法

    过程就不分析了,只说解决方法: 1.给a标签添加样式:background:url(about:blank); 2.给a标签随便添加背景色或者背景图片,然后将a标签的透明度设置为0,不过在IE中需要使 ...