Shiro源码解析-Session篇
上一篇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篇的更多相关文章
- Shiro源码解析-登录篇
本文以循序渐进的方式解析Shiro整个login过程的处理,读者可以边阅读本文边自己看代码来思考体会,如有问题,欢迎评论区探讨! 笔者shiro的demo源码路径:https://github.com ...
- jQuery2.x源码解析(缓存篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...
- jQuery2.x源码解析(构建篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...
- jQuery2.x源码解析(设计篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...
- jQuery2.x源码解析(回调篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...
- myBatis源码解析-类型转换篇(5)
前言 开始分析Type包前,说明下使用场景.数据构建语句使用PreparedStatement,需要输入的是jdbc类型,但我们一般写的是java类型.同理,数据库结果集返回的是jdbc类型,而我们需 ...
- Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- myBatis源码解析-数据源篇(3)
前言:我们使用mybatis时,关于数据源的配置多使用如c3p0,druid等第三方的数据源.其实mybatis内置了数据源的实现,提供了连接数据库,池的功能.在分析了缓存和日志包的源码后,接下来分析 ...
- myBatis源码解析-反射篇(4)
前沿 前文分析了mybatis的日志包,缓存包,数据源包.源码实在有点难顶,在分析反射包时,花费了较多时间.废话不多说,开始源码之路. 反射包feflection在mybatis路径如下: 源码解析 ...
随机推荐
- HUST软工1501-1503班第2周作业成绩公布
说明 本次公布的成绩对应的作业为: 第2周个人作业:WordCount编码和测试 如果同学对作业成绩存在异议,在成绩公布的72小时内(截止日期4月26日0点)可以进行申诉,方式如下: 毕博平台的第二周 ...
- C#编程之程序集和反射
这里我又唠叨几句,大家在学习的时候,如看书或者看视频时觉得非常爽,因为感觉基本都看得懂也都挺容易的,其实看懂是一回事,你自己会动手做出来是一回事,自己能够说出来又是另一回事了.应该把学到的东西变成自己 ...
- Word文档如何发CSDN博客
目前大部分的博客作者在写博客这件事情上都会遇到以下3个痛点:1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.2.发布到博客或公众号平台 ...
- Web图片编辑控件发布-Xproer.ImageEditor
版权所有 2009-2014 荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com 产品首页:http://www.ncmem.com/webplug/image-e ...
- CKEditor4.x整合教程-Xproer.WordPaster
版权所有 2009-2017 荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webplug/wordpa ...
- 团体程序设计天梯赛L1-022 奇偶分家 2017-03-22 17:48 81人阅读 评论(0) 收藏
L1-022. 奇偶分家 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 给定N个正整数,请统计奇数和偶数各有多少个? 输入格式 ...
- WPF 简洁的主界面
用的是dev的TileLayouotControl控件. <dxwui:PageAdornerControl Header="" Padding="30" ...
- cookie的优缺点
优点 :极高的扩展性和可用性 1.通过良好的编程,控制保存在cookie中的session对象的大小. 2.通过加密和安全传输技术(ssl),减少cookie被破解的可能性 3.只有cookie中存 ...
- 【后缀数组之height数组】
模板奉上 int rank[maxn],height[maxn]; void calheight(int *r,int *sa,int n) { ; ;i<=n;i++) rank[sa[i]] ...
- [转贴]VC编译器版本号_MSC_VER and _MSC_FULL_VER
Visual Studio version and discrimination macros Abbreviation Product name [Visual Studio version] †1 ...