一句话总结:会话域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使用工厂模式来获得SecurityManager的实例,并提供了一个Factory接口,通过泛型定义了getInstance()方法。这允许Shiro通过不同工厂来创建不同的...
课程中提供的源码讲解将帮助你深入理解Shiro的内部工作原理。通过分析源码,你可以看到Shiro如何处理认证、授权流程,以及它是如何与其他组件协同工作的。课件则会系统地介绍Shiro的关键概念和实践案例,帮助你巩固...
通过提供的源码,开发者可以深入理解Shiro的内部工作原理,学习如何定制化Shiro以满足特定需求。 9. **开发文档**: 开发文档通常包含了Shiro的安装指南、API参考、使用示例等内容,帮助开发者快速上手并解决实际...
深入学习Shiro的源码能帮助我们理解其内部工作原理。`Apache_Shiro参考手册中文版`和`深入浅出学Shiro`这两本书籍将引导你逐步了解Shiro的架构设计、核心组件的实现细节以及如何扩展Shiro以满足特定需求。通过阅读`...
对于开发者来说,通过学习 Shiro 的源码,可以深入理解其内部工作原理,提高安全编程的能力。在这个 Shiro1.2.2 的源码包中,我们可以探索以下几个关键知识点: 1. **身份认证(Authentication)**:Shiro 提供了...
在学习Shiro时,源码分析是一个深入了解其内部工作原理的有效方式。本文将继续上一篇文章的案例,深入分析Shiro的Subject和Session机制,以及SecurityManager在创建Subject时所扮演的角色和过程。 首先,Shiro中的...
在本压缩包中,你获得了Shiro框架的核心部分——shirocore1.7的源代码,版本号为1.7.0,这将对理解Shiro的工作原理及其内部机制大有裨益。 Shiro 的核心组件主要包括以下几个方面: 1. **Authentication(认证)**...
通过分析源代码,我们可以深入理解Shiro的工作原理和设计模式。 1. **认证**: Shiro 的认证过程包括身份验证(Authentication)和登录(Login)。身份验证涉及Subject、Realm和CredentialsMatcher等核心组件。...
Apache Shiro 是一个强大且易用...通过深入源码,你可以更清楚地了解Shiro的内部工作原理,从而更好地利用它来增强你的应用安全性。同时,对于Java开发者来说,阅读高质量的开源项目源码也是一种提升编程技能的好方法。
这个压缩包“shiro-root-1.2.3-source-release”包含了Shiro 1.2.3版本的源代码,并且带有注释,这对于理解Shiro的工作原理和自定义扩展是非常有价值的。 1. **Shiro框架概述** Shiro设计的核心理念是简洁,它将...
了解Shiro的工作原理,我们可以通过阅读源码来深入理解其内部实现。Shiro的认证和授权流程主要集中在`DefaultSubjectExecutor`、`AuthenticationManager`、`AuthorizingRealm`等类中。这些类通过回调接口和事件驱动...
"跟我学Shiro源码"这个主题旨在深入理解Shiro的工作原理,通过分析源码来提升我们对安全编程的认知。 1. **Shiro架构** Shiro 的核心组件包括 Realm(认证与授权信息源)、SecurityManager(安全管理器)、Subject...
通过尚硅谷Shiro源码,我们可以看到这些功能是如何在源码层面实现的,有助于深入理解Shiro的内部工作原理,从而在实际开发中更好地利用和定制它。此外,分析源码也有助于提升我们对于Java安全框架的设计和实现的理解...
通过阅读源码,你可以深入理解其内部工作原理,比如认证过程、授权逻辑等。 8. **工具支持**: Shiro 还提供了命令行工具,如 `shiro-standalone`,方便开发者在没有 Web 环境的情况下进行测试和调试。 入门教程...
学习Shiro 1.2.4 的源码,可以帮助开发者深入理解其内部工作原理,从而更好地定制和扩展功能,提升应用安全性。同时,对加密算法、会话管理、权限控制等核心模块的理解,将有助于提升开发者在安全领域的专业素养。 ...
对于源码分析,可以深入理解Shiro的工作原理,有助于定制化开发和扩展。同时,可以参考官方的示例代码或社区的实践案例,快速上手并解决实际项目中的安全问题。 总之,Apache Shiro-1.9.0版本作为一个全面的安全...
而【压缩包子文件的文件名称列表】中的“kwan02251353”可能是一个文件夹名或压缩包内部的归档标识,具体的文件结构和内容未给出,因此无法详细分析。 总的来说,这个图书管理系统是基于现代Web开发技术构建的,...
由于没有具体的文件列表,我们无法详细分析 Shiro-Memshell 的内部结构和使用方法。但可以推测,这个工具可能包含源代码、配置文件、示例用法、文档等组成部分。对于使用和学习 Shiro-Memshell,理解 Shiro 的工作...
Jeecg开发平台是一款基于Java技术的企业级快速开发框架,其源码的提供使得开发者能够深入理解其工作原理,进一步定制化开发或者进行二次开发。这个框架的核心目标是提高开发效率,降低维护成本,实现敏捷开发。以下...