上一篇Shiro源码解析-登录篇中提到了在登录验证成功后有对session的处理,但未详细分析,本文对此部分源码详细分析下。

1. 分析切入点:DefaultSecurityManger的login方法

    public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info;
try {
info = authenticate(token);
} catch (AuthenticationException ae) {
try {
onFailedLogin(token, ae, subject);
} catch (Exception e) {
if (log.isInfoEnabled()) {
log.info("onFailedLogin method threw an " +
"exception. Logging and propagating original AuthenticationException.", e);
}
}
throw ae; //propagate
} Subject loggedIn = createSubject(token, info, subject); // 登录用户验证成功之后进行session处理 onSuccessfulLogin(token, info, loggedIn); return loggedIn;
}

继续DefaultSecurityManger

    protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {
SubjectContext context = createSubjectContext();
context.setAuthenticated(true);
context.setAuthenticationToken(token);
context.setAuthenticationInfo(info);
if (existing != null) {
context.setSubject(existing);
}
return createSubject(context); // 此处创建Subject
}

DefaultSecurityManger的createSubject方法

    public Subject createSubject(SubjectContext subjectContext) {
//create a copy so we don't modify the argument's backing map:
SubjectContext context = copy(subjectContext); //ensure that the context has a SecurityManager instance, and if not, add one:
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:
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:
context = resolvePrincipals(context); 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:
save(subject); // 在这一步存储session return subject;
}
    protected void save(Subject subject) {
this.subjectDAO.save(subject);
}

2. 转移到DefaultSubjectDAO
  调用DefaultSubjectDAO.save(subject)方法

    public Subject save(Subject subject) {
if (isSessionStorageEnabled(subject)) { // 默认sessionStorage是enabled
saveToSession(subject); // 看这里
} else {
log.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " +
"authentication state are expected to be initialized on every request or invocation.", subject);
} return subject;
}

调用DefaultSubjectDAO.saveToSession(subject)方法

   protected void saveToSession(Subject subject) {
//performs merge logic, only updating the Subject's session if it does not match the current state:
mergePrincipals(subject);
mergeAuthenticationState(subject);
}

调用DefaultSubjectDAO的mergePrincipals方法

   protected void mergePrincipals(Subject subject) {
//merge PrincipalCollection state: PrincipalCollection currentPrincipals = null; //SHIRO-380: added if/else block - need to retain original (source) principals
//This technique (reflection) is only temporary - a proper long term solution needs to be found,
//but this technique allowed an immediate fix that is API point-version forwards and backwards compatible
//
//A more comprehensive review / cleaning of runAs should be performed for Shiro 1.3 / 2.0 +
if (subject.isRunAs() && subject instanceof DelegatingSubject) {
try {
Field field = DelegatingSubject.class.getDeclaredField("principals");
field.setAccessible(true);
currentPrincipals = (PrincipalCollection)field.get(subject);
} catch (Exception e) {
throw new IllegalStateException("Unable to access DelegatingSubject principals property.", e);
}
}
if (currentPrincipals == null || currentPrincipals.isEmpty()) {
currentPrincipals = subject.getPrincipals();
} Session session = subject.getSession(false); // 取得session,如果不存在,并不会主动创建session if (session == null) {
if (!isEmpty(currentPrincipals)) {
session = subject.getSession(); // 第一次用户访问时,会创建session,那么session是如何创建的呢?在缓存还是在DB中,请继续往下看
session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);
}
// otherwise no session and no principals - nothing to save
} else {
PrincipalCollection existingPrincipals =
(PrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); if (isEmpty(currentPrincipals)) {
if (!isEmpty(existingPrincipals)) {
session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
}
// otherwise both are null or empty - no need to update the session
} else {
if (!currentPrincipals.equals(existingPrincipals)) {
session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);
}
// otherwise they're the same - no need to update the session
}
}
}

3. 转移到DelegatingSubject

调用DelegatingSubject的getSession

    public Session getSession() {
return getSession(true); // 参数为true,表示不存在此session时创建
} public Session getSession(boolean create) {
if (log.isTraceEnabled()) {
log.trace("attempting to get session; create = " + create +
"; session is null = " + (this.session == null) +
"; session has id = " + (this.session != null && session.getId() != null));
} if (this.session == null && create) { //added in 1.2:
if (!isSessionCreationEnabled()) {
String msg = "Session creation has been disabled for the current subject. This exception indicates " +
"that there is either a programming error (using a session when it should never be " +
"used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +
"for the current Subject. See the " + DisabledSessionException.class.getName() + " JavaDoc " +
"for more.";
throw new DisabledSessionException(msg);
} log.trace("Starting session for host {}", getHost());
SessionContext sessionContext = createSessionContext();
Session session = this.securityManager.start(sessionContext); // session创建,从这里再往下走(这里的securityMananger是SessionSecurityManager)
this.session = decorate(session);
}
return this.session;
}

4. 转移到SessionSecurityManager

    public Session start(SessionContext context) throws AuthorizationException {
return this.sessionManager.start(context); // 如果没有设置sessionManager,则调用默认的ServletContainerSessionManager(内存存储session),demo代码使用如下
}

5. 如果像demo中soure一样,设置了的sessionManager为DefaultWebSessionManager,那么接下来会转移为到它的父类AbstractValidatingSessionManager

    protected Session createSession(SessionContext context) throws AuthorizationException {
enableSessionValidationIfNecessary();
return doCreateSession(context);
} protected Session doCreateSession(SessionContext context) {
Session s = newSessionInstance(context);
if (log.isTraceEnabled()) {
log.trace("Creating session for host {}", s.getHost());
}
create(s); // 在此处创建Session
return s;
} protected Session newSessionInstance(SessionContext context) {
return getSessionFactory().createSession(context);
}

6. 调用DefaultSessionManager

    protected void create(Session session) {
if (log.isDebugEnabled()) {
log.debug("Creating new EIS record for new session instance [" + session + "]");
}
sessionDAO.create(session); // 到这里大家应该看到了,你配置的SessionDAO在什么时候调用,demo中使用EnterpriseCacheSessionDAO
}

7. 先调用的父类CachingSessionDAO的create方法

    public Serializable create(Session session) {
Serializable sessionId = super.create(session);// 这里只是生成sessionId,至于生成使用的算法可以在config中设置sessionIdGenerator
cache(session, sessionId); // 生成session并存储到cache,过程在下面
return sessionId;
}

8. 接下来cache方法

    protected void cache(Session session, Serializable sessionId) {
if (session == null || sessionId == null) {
return;
}
Cache<Serializable, Session> cache = getActiveSessionsCacheLazy(); // 创建cache,名字默认为shiro-activeSessionCache
if (cache == null) {
return;
}
cache(session, sessionId, cache); // 存储到cache
}
   protected void cache(Session session, Serializable sessionId, Cache<Serializable, Session> cache) {
cache.put(sessionId, session); // 这里调用的Ehcache的put方法,最终是存储在cache中(当然,如果你自定义了SessionDAO,那就可以存储在你指定的地方)
}

到这里大家基本都明白了整个过程吧,通过源码分析我们可以明白以下关键点

  • SessionManager什么时候调用
  • SessionDAO何时调用
  • SessionId什么时候生成
  • Session什么时候存储到Cache

如有问题,欢迎评论回复!

Shiro源码解析-Session篇的更多相关文章

  1. Shiro源码解析-登录篇

    本文以循序渐进的方式解析Shiro整个login过程的处理,读者可以边阅读本文边自己看代码来思考体会,如有问题,欢迎评论区探讨! 笔者shiro的demo源码路径:https://github.com ...

  2. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  3. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  4. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  5. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

  6. myBatis源码解析-类型转换篇(5)

    前言 开始分析Type包前,说明下使用场景.数据构建语句使用PreparedStatement,需要输入的是jdbc类型,但我们一般写的是java类型.同理,数据库结果集返回的是jdbc类型,而我们需 ...

  7. Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  8. myBatis源码解析-数据源篇(3)

    前言:我们使用mybatis时,关于数据源的配置多使用如c3p0,druid等第三方的数据源.其实mybatis内置了数据源的实现,提供了连接数据库,池的功能.在分析了缓存和日志包的源码后,接下来分析 ...

  9. myBatis源码解析-反射篇(4)

    前沿 前文分析了mybatis的日志包,缓存包,数据源包.源码实在有点难顶,在分析反射包时,花费了较多时间.废话不多说,开始源码之路. 反射包feflection在mybatis路径如下: 源码解析 ...

随机推荐

  1. 路飞项目背景,contentType以及django缓存

    昨日回顾: 分页器: 普通分页 # 普通分页 from rest_framework.pagination import PageNumberPagination -每页的大小(默认) -查询的时候, ...

  2. Ubuntu14.04 下安装Vmware-Tools

    1.切换到ubuntu 图形界面 startx , 点击虚拟机菜单栏-安装VMware Tools 2. 在Ubuntu系统中找到VMwareTools-9.2.2-893683.tar.gz ,右键 ...

  3. C#6.0特性(快来围观)(转)

    出处:http://www.cnblogs.com/HJL-Blog/p/4457632.html 说明一下,很多博友一进来就认为仅仅是语法糖,C#语法的更新,代表着它的进步,语法糖是为了让我们更好的 ...

  4. Git & Github使用总结

    Linux下git的安装 在终端下输入 git , 看系统有没有安装git. 如果没有安装则会出现以下提醒: The program 'git' is currently not installed. ...

  5. WordPaster2-Chrome 45安装说明

    下载地址:http://www.ncmem.com/download/WordPaster2/WordPaster.exe 安装方法:   运行WordPaster.exe进行安装,WordPaste ...

  6. ComponentSpace SAML v2.0 for .NET 使用介绍

    下载地址:http://samlsso.codeplex.com/ 以下描叙参考版本为其官网最新版本2.5.0.6.相对2.4版本,2.5有了很大改进,很多接口方法都变了.使用起来更方便,易懂. 广告 ...

  7. telnet ip port

    windows测试远程端口服务是否能连接上:telnet ip port windows7 系统需要手动启用telnet功能,如下图:

  8. Android-Activity-startActivityForResult

    之前 Android-Intent意图传递数据,的博客讲解了,一个Activity 跳转 到另外一个Activity 可以把数据带过去 Android还提供了一种方式,一个Activity 跳转 到另 ...

  9. django使用ajax传输数据

    HTML文件ajax get例子 <!DOCTYPE html> <html lang="en"> <head> <meta charse ...

  10. pycharm注册

    1.官方下载专业版,并安装 http://www.jetbrains.com/pycharm/download/#section=windows 2.下载crack激活包 http://idea.la ...