`
ahua186186
  • 浏览: 561936 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

shiro内部原理分析

 
阅读更多

一句话总结:会话域Context一路收集principals, authenticated, host, session(readSession()返回), sessionEnabled, request, response, securityManager ; 最终被存入到了返回的这个Subject中

 

1.第一步:ShiroFilterFactoryBean的初始化和创建
(1)实现BeanPostProcessor接口:具体看源码postProcessBeforeInitialization
实现的功能:将在Spring中注册的Filter并入到ShiroFilterFactoryBean的filters中,
目标:保证不遗漏Filter
(2)实现FactoryBean<T>接口:具体看源码getObject()
实现的功能: 返回AbstractShiroFilter实例,本质是个过滤器(extends OncePerRequestFilter ),所以应用必须运行在servlet容器中
-->确保用户配置了SecurityManager
-->createFilterChainManager():
-->获取默认过滤器,为默认加载的Filter,应用上全局配置属性(applyGlobalPropertiesIfNecessary)

public enum DefaultFilter {
    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class);
    ....
    }

 
   -->处理用户配置的ShiroFilterFactoryBean.filterChainDefinitions属性,添加到DefaultFilterChainManager对象的filterChains属性中
-->构造SpringShiroFilter实例,由Spring管理,具体看源码createInstance();
2.第二步:处理1个http请求原理
(1)1个请求1个createSubject原理:由于ShiroFilterFactoryBean本质是个AbstractShiroFilter过滤器,所以每次请求都会执行doFilterInternal里面的createSubject方法。
-->createSubject逻辑比较复杂,单独梳理。
(2)最终被并入到Servlet的FilterChain中的Filter实例为ShiroFilterFactoryBean.SpringShiroFilter类型
(3)doFilterInternal中的核心处理逻辑:executeChain

subject.execute(new Callable() {
                public Object call() throws Exception {
                    AbstractShiroFilter.this.updateSessionLastAccessTime(request, response);
                    AbstractShiroFilter.this.executeChain(request, response, chain);
                    return null;
                }
            });

 
-->根据请求的request找到requestURI ,比如 以 http://xxx/myapp/my/test.jsp举例的话;这里返回的requestURI, 其值为 /my/test.jsp
-->筛选出与当前请求链接匹配的FilterChain:
DefaultFilterChainManager类中Map<String, Filter>类型的字段 filters ,它里面存储的是默认的Filter + 我们自定义注册的Filter
-->一旦匹配, 则对Servlet的FilterChain进行代理。保证Shiro自定义的Filter执行完毕之后,再执行Servlet的Filter。
-->总结:
Shiro直接将整个FilterChain代理,先执行完我的FilterChain,才考虑Servlet的。
Shiro 对Servlet 容器的FilterChain 进行了代理,即ShiroFilter 在继续Servlet 容器的Filter链的执行之前,
通过ProxiedFilterChain`对Servlet 容器的FilterChain 进行了代理;即先走Shiro 自己的Filter 体系,
然后才会委托给Servlet 容器的FilterChain 进行Servlet 容器级别的Filter链执行; 《跟开涛学Shiro》
每次请求的到来,Shiro都会从我们配置到org.apache.shiro.spring.web.ShiroFilterFactoryBean的filterChainDefinitions中
挑选一个匹配过滤链(多个匹配也只会选择第一个匹配的),Shiro会执行这个链条,最后采取执行其他Servlet Filter
-->
3.第三步:shiro的扩展
-->都是向FilterChain中插入自定义Filter; 玩的都是过滤器,简单好用。。。。

-->扩展CachingSessionDAO,实现session的自定义存取

-->AuthenticationListener

-->LogoutAware

-->自定义SimpleSession,新增一些属性,比如在线状态等

-->自定义SessionFactory,初始化自定义的SimpleSession


4.补充:createSubject逻辑:
-->源码入口:

// AbstractShiroFilter.createSubject
protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
    // securityManager是shiro强制要求用户必须自己配置的。
    // 在Web环境下,其为DefaultWebSecurityManager类型。这一点随便找个spring-shiro的配置文件就能看到了。
    return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
}
-->WebSubject.Builder构造函数:
public Builder(SecurityManager securityManager, ServletRequest request, ServletResponse response) {
    // 调用Subject.Builder的构造函数
    super(securityManager);
    if (request == null) {
        throw new IllegalArgumentException("ServletRequest argument cannot be null.");
    }
    if (response == null) {
        throw new IllegalArgumentException("ServletResponse argument cannot be null.");
    }
    // 让SubjectContext会话域附加上当前请求request; 贯穿整个执行过程。
    setRequest(request);
    // 让SubjectContext会话域附加上当前响应response; 贯穿整个执行过程。
    setResponse(response);
}

// Subject.Builder 构造函数
public Builder(SecurityManager securityManager) {
    if (securityManager == null) {
        throw new NullPointerException("SecurityManager method argument cannot be null.");
    }
    this.securityManager = securityManager;
    // 构建一个SubjectContext会话域。
    this.subjectContext = newSubjectContextInstance();
    if (this.subjectContext == null) {
        throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
                "cannot be null.");
    }

    // 让会话域带着securityManager贯穿整个执行过程。
    this.subjectContext.setSecurityManager(securityManager);
}

 
-->以上是初始化上下文,然后哒哒哒。。快马加鞭。,我们直接跳到WebSubject.Builder.buildWebSubject:
public Subject buildSubject() {
    // 委托给了SecurityManager实例;
    // 这里的securityManager实际类型为DefaultWebSecurityManager类型
    // 而subjectContext的时机类型为DefaultWebSubjectContext, 而且按照之前的跟踪,
    //我们知道该Context中已经被填入了当前请求的request,response以及securityManager实例
    return this.securityManager.createSubject(this.subjectContext);
}
总之就是把创建subject的操作直接委托给了SecurityManager实例创建subject(携带subjectContext,这个上下文很重要)
-->
会话域Context一路收集来的principals, authenticated, host, session, sessionEnabled, request, response, securityManager ; 最终被存入到了返回的这个Subject中
--> 核心:DefaultSecurityManager.createSubject:

public Subject createSubject(SubjectContext subjectContext) {
    //create a copy so we don't modify the argument's backing map:
    // copy方法被子类DefaultWebSecurityManager重载; 返回一个DefaultWebSubjectContext实例
    SubjectContext context = copy(subjectContext);

    //ensure that the context has a SecurityManager instance, and if not, add one:
    // 子类DefaultWebSecurityManager未进行重载, 
    // 此方法确保会话域持有一个SecurityManager来贯穿整个执行流程。
    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:
    // 向会话域中存入一个Session实例; 
    // 此操作可能失败, 届时会构建一个缺少Session的会话域 
    // 注意这里的构建Session出错是被允许的, 所以异常是以Debug的方式输出的.
    // Session的维护是交给了专门的SessionManager来负责
    // 注意这里用的是SessionKey类型的Key,而不是简单的string类型的sessionId
    // 因为Session我们操作的比较频繁,所以下文会进行详解
    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:
    // 这一步会向会话域中插入Principal; 此操作也有可能失败, 即最终会话域context中缺少Principal信息
    // rememberMe的功能也是交给了专门的RememberMeManager
    // 而且默认的RememberMe功能是通过Cookie来完成的, 所以默认的实现是CookieRememberMeManager; 而且cookie的默认名称是rememberMe
    // 而且Shiro有自己专门的Cookie接口,而唯一的实现则是SimpleCookie
    context = resolvePrincipals(context);

    // 看过Spring源码的都知道这命名意味着什么, 真正干活的来了。
    // 创建Subject的工作又被委派给了专门的SubjectFactory, 七拐八绕啊。
    // SubjectFactory接口的默认实现为DefaultWebSubjectFactory
    // 观察其对createSubject方法的实现正式将会话域context这一路收集来的信息汇总生成一个WebDelegatingSubject实例(又增加一个中间层)。
    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:
    // 专门的SubjectDAO接口负责对该subject进行保存操作
    // SubjectDAO接口的默认实现类为DefaultSubjectDAO
    save(subject);

    return subject;
}

 

public class DefaultWebSubjectFactory extends DefaultSubjectFactory {
    public DefaultWebSubjectFactory() {
    }

    public Subject createSubject(SubjectContext context) {
        if (!(context instanceof WebSubjectContext)) {
            return super.createSubject(context);
        } else {
            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);
        }
    }
...
}

 
-->context = resolveSession(context)分析
最终是执行:DefaultSessionManager类中的一段逻辑:retrieveSessionFromDataSource:
看到这个方法的代码我们就知道是通过外部的sessionDao去读了,而这个Dao恰好是我们经常扩展的Dao,比如改成从redis存取数据

protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
        Serializable sessionId = this.getSessionId(sessionKey);
        if (sessionId == null) {
            log.debug("Unable to resolve session ID from SessionKey [{}].  Returning null to indicate a session could not be found.", sessionKey);
            return null;
        } else {
            Session s = this.retrieveSessionFromDataSource(sessionId);
            if (s == null) {
                String msg = "Could not find session with ID [" + sessionId + "]";
                throw new UnknownSessionException(msg);
            } else {
                return s;
            }
        }
    }
    
    protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException {
        return this.sessionDAO.readSession(sessionId);
    }

 
   
    -->context = this.resolvePrincipals(context);分析:
    从RememberMeManager中获取凭证

 

分享到:
评论

相关推荐

    shiro源码分析

    然而,通过源码分析可以更深入地理解Shiro的内部工作原理。 Shiro使用工厂模式来获得SecurityManager的实例,并提供了一个Factory接口,通过泛型定义了getInstance()方法。这允许Shiro通过不同工厂来创建不同的...

    Shiro入门到精通

    课程中提供的源码讲解将帮助你深入理解Shiro的内部工作原理。通过分析源码,你可以看到Shiro如何处理认证、授权流程,以及它是如何与其他组件协同工作的。课件则会系统地介绍Shiro的关键概念和实践案例,帮助你巩固...

    Shiro安全验证框架

    通过提供的源码,开发者可以深入理解Shiro的内部工作原理,学习如何定制化Shiro以满足特定需求。 9. **开发文档**: 开发文档通常包含了Shiro的安装指南、API参考、使用示例等内容,帮助开发者快速上手并解决实际...

    shiro资料shiro资料

    深入学习Shiro的源码能帮助我们理解其内部工作原理。`Apache_Shiro参考手册中文版`和`深入浅出学Shiro`这两本书籍将引导你逐步了解Shiro的架构设计、核心组件的实现细节以及如何扩展Shiro以满足特定需求。通过阅读`...

    Shiro1.2.2_源码(压缩包)

    对于开发者来说,通过学习 Shiro 的源码,可以深入理解其内部工作原理,提高安全编程的能力。在这个 Shiro1.2.2 的源码包中,我们可以探索以下几个关键知识点: 1. **身份认证(Authentication)**:Shiro 提供了...

    shiro源码分析二

    在学习Shiro时,源码分析是一个深入了解其内部工作原理的有效方式。本文将继续上一篇文章的案例,深入分析Shiro的Subject和Session机制,以及SecurityManager在创建Subject时所扮演的角色和过程。 首先,Shiro中的...

    shiro-root-1.7.0_ROOT_shirocore1.7_shiro1.7源码_

    在本压缩包中,你获得了Shiro框架的核心部分——shirocore1.7的源代码,版本号为1.7.0,这将对理解Shiro的工作原理及其内部机制大有裨益。 Shiro 的核心组件主要包括以下几个方面: 1. **Authentication(认证)**...

    shiro源文件

    通过分析源代码,我们可以深入理解Shiro的工作原理和设计模式。 1. **认证**: Shiro 的认证过程包括身份验证(Authentication)和登录(Login)。身份验证涉及Subject、Realm和CredentialsMatcher等核心组件。...

    shiro源码 1.2正式版 编译通过

    Apache Shiro 是一个强大且易用...通过深入源码,你可以更清楚地了解Shiro的内部工作原理,从而更好地利用它来增强你的应用安全性。同时,对于Java开发者来说,阅读高质量的开源项目源码也是一种提升编程技能的好方法。

    shiro-root-1.2.3-source-release源码带注释.rar

    这个压缩包“shiro-root-1.2.3-source-release”包含了Shiro 1.2.3版本的源代码,并且带有注释,这对于理解Shiro的工作原理和自定义扩展是非常有价值的。 1. **Shiro框架概述** Shiro设计的核心理念是简洁,它将...

    shiro认证授权的过程

    了解Shiro的工作原理,我们可以通过阅读源码来深入理解其内部实现。Shiro的认证和授权流程主要集中在`DefaultSubjectExecutor`、`AuthenticationManager`、`AuthorizingRealm`等类中。这些类通过回调接口和事件驱动...

    跟我学Shiro源码

    "跟我学Shiro源码"这个主题旨在深入理解Shiro的工作原理,通过分析源码来提升我们对安全编程的认知。 1. **Shiro架构** Shiro 的核心组件包括 Realm(认证与授权信息源)、SecurityManager(安全管理器)、Subject...

    尚硅谷Shiro源码

    通过尚硅谷Shiro源码,我们可以看到这些功能是如何在源码层面实现的,有助于深入理解Shiro的内部工作原理,从而在实际开发中更好地利用和定制它。此外,分析源码也有助于提升我们对于Java安全框架的设计和实现的理解...

    shiro入门

    通过阅读源码,你可以深入理解其内部工作原理,比如认证过程、授权逻辑等。 8. **工具支持**: Shiro 还提供了命令行工具,如 `shiro-standalone`,方便开发者在没有 Web 环境的情况下进行测试和调试。 入门教程...

    Shrio 1.24 源码

    学习Shiro 1.2.4 的源码,可以帮助开发者深入理解其内部工作原理,从而更好地定制和扩展功能,提升应用安全性。同时,对加密算法、会话管理、权限控制等核心模块的理解,将有助于提升开发者在安全领域的专业素养。 ...

    shiro-1.9.0版本jar包

    对于源码分析,可以深入理解Shiro的工作原理,有助于定制化开发和扩展。同时,可以参考官方的示例代码或社区的实践案例,快速上手并解决实际项目中的安全问题。 总之,Apache Shiro-1.9.0版本作为一个全面的安全...

    管理系统系列--简单的图书管理系统。springboot+shiro+thymeleaf+Layui.zip

    而【压缩包子文件的文件名称列表】中的“kwan02251353”可能是一个文件夹名或压缩包内部的归档标识,具体的文件结构和内容未给出,因此无法详细分析。 总的来说,这个图书管理系统是基于现代Web开发技术构建的,...

    Shiro-Memshell

    由于没有具体的文件列表,我们无法详细分析 Shiro-Memshell 的内部结构和使用方法。但可以推测,这个工具可能包含源代码、配置文件、示例用法、文档等组成部分。对于使用和学习 Shiro-Memshell,理解 Shiro 的工作...

    jeecg开发平台源码

    Jeecg开发平台是一款基于Java技术的企业级快速开发框架,其源码的提供使得开发者能够深入理解其工作原理,进一步定制化开发或者进行二次开发。这个框架的核心目标是提高开发效率,降低维护成本,实现敏捷开发。以下...

Global site tag (gtag.js) - Google Analytics