一句话总结:会话域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中获取凭证
相关推荐
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。 Realm: Realm充当了Shiro与应用安全数据间的"桥梁"或者"连接器"。也...
shiro 内存模型分析
分析shiro框架+cas单点登录系统的技术分析,解析了相关的技术难点
尚硅谷_Shiro_认证思路分析 · 08.尚硅谷_Shiro_实现认证流程 · 09.尚硅谷_Shiro_实现认证 Realm · 10.尚硅谷_Shiro_密码的比对 · 11.尚硅谷_Shiro_密码的MD5加密 · 12. 尚硅谷_Shiro_密码的MD5盐值加密 ...
shiro shiro-core-1.7.1 jar shiro漏洞
shiro入门 安全校验
shiro使用的版本是1.2.4,存在反序列化漏洞,我们采取的办法是手动升级到了1.2.6版本,但苦于无法验证是否解决了问题,后来发现了一款测试工具,ShiroExploit。 测试工具下载地址 ... 反序列化漏洞是如何产生的?...
shiro(shiro-all-1.8.0.jar)
shiro最简单整合版本shiro最简单整合版本shiro最简单整合版本shiro最简单整合版本shiro最简单整合版本
freemarker的shiro标签
shiro所有的jar包 还有跟cas集成的jar包 以及shiro官网的jar下载地址
Apache_Shiro_使用手册(一)Shiro架构介绍
shiro权限demo
-- 保证实现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- AOP式方法级权限检查 --> ...
shiro入门及深入,对于想学习shiro的同学,是不错的学习资料。
Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大. Shiro 可以非常容易的开发出足够好的应用,其不仅...
java集成shiro框架,全jar 包,java集成shiro框架java集成shiro框架java集成shiro框架java集成shiro框架java集成shiro框架java集成shiro框架java集成shiro框架java集成shiro框架
跟我学shiro的书和例子源码,非常适合学习shiro者使用
赠送jar包:shiro-core-1.4.0.jar; 赠送原API文档:shiro-core-1.4.0-javadoc.jar; 赠送源代码:shiro-core-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-core-1.4.0.pom; 包含翻译后的API文档:shiro-core...
Shiro是Apache从JSecret项目演变而来的,该框架实现了:用户登录、认证、授权和权限管理操作的完整控制流程。Shiro最早的名字是JSecurity,后来更名为Shiro并成为Apache的孵化项目。这次改名也同样影响了Grails ...