shiro源码篇-shiro的ssion的查询、刷新、过期与删除,你值得拥有
holly开⼼⼀刻
⽼公酷爱⽹络游戏,⽼婆⽆奈,只得告诫他:你玩就玩了,但是千万不可以在游戏⾥找⽼婆,不然,哼哼。。。
⽼公嘴⾓露出了微笑:放⼼吧亲爱的,我绝对不会在游戏⾥找⽼婆的!因为我有⽼公!
⽼婆:......
前情回顾
⼤家还记得讲了什么吗,我们来⼀起简单回顾下:
SecurityManager是shiro的核⼼,负责与shiro的其他组件进⾏交互;SessionManager是ssion的真正管理者,负责shiro的ssion管理;
SessionsSecurityManager的start⽅法中将ssion的创建委托给了具体的ssionManager,是创建ssion的关键⼊⼝。
SimpleSession是shiro完完全全的⾃⼰实现,是shiro对ssion的⼀种拓展;实现了ValidatingSession接⼝,具有⾃我校验的功能;⼀般不对外暴露,暴露的往往是他的代理:DelegatingSession;SimpleSession有⼏个属性值得重点关注下,如下
id:就是ssion id;
startTimestamp:ssion的创建时间;
stopTimestamp:ssion的失效时间;
lastAccessTime:ssion的最近⼀次访问时间,初始值是startTimestamp
timeout:ssion的有效时长,默认30分钟
expired:ssion是否到期
attributes:ssion的属性容器
查询
ssion的创建完成后,会将ssion(SimpleSession类型)对象的代理对象(DelegatingSession)
装饰成StoppingAwareProxiedSession对象,然后绑定到subject(类型是DelegatingSubject);
Session ssion = Session();返回的就是绑定在当前subjuct的ssion。注意subject的实际类型是:DelegatingSubject,如下图
刷新
water pollution shiro的Session接⼝提供了⼀个touch⽅法,负责ssion的刷新;ssion的代理对象最终会调⽤SimpleSession的touch():
public void touch() {
this.lastAccessTime = new Date(); // 更新最后被访问时间为当前时间
荛花
}
但是touch⽅法是什么时候被调⽤的呢?JavaSE需要我们⾃⼰定期的调⽤ssion的touch() 去更新最后访问时间;如果是Web应⽤,每次进⼊ShiroFilter都会⾃动调⽤uch()来更新最后访问时间,ShiroFilter的类图如下:
ShiroFilter⾃动调⽤uch()如下
西安美容培训
过期
如果是让我们⾃⼰实现ssion过期的判断,我们会怎么做了?我们来看看shiro是怎么做的,或许我们能够从中学到⼀些经验。
韩语转换器
还记得AbstractValidatingSessionManager中createSession⽅法吗?在调⽤doCreateSession⽅法之前调⽤
enableSessionValidationIfNecessary(),enableSessionValidationIfNecessary代码如下
private void enableSessionValidationIfNecessary() {
// 获取ssion验证调度器
SessionValidationScheduler scheduler = getSessionValidationScheduler();
// ssion验证调度器开启 && (调度器为空或调度器不可⽤)
马丁路德金简介if (isSessionValidationSchedulerEnabled() && (scheduler == null || !scheduler.isEnabled())) {
enableSessionValidation(); // 开启ssion验证
}
}
View Code
thought怎么读 第⼀次创建ssion的时候,如果ssion验证调度器启⽤(默认是启⽤),那么调⽤enableSessionValidation(),enableSessionValidation代码如下
protected synchronized void enableSessionValidation() {
SessionValidationScheduler scheduler = getSessionValidationScheduler(); // 获取调取器
if (scheduler == null) {
scheduler = createSessionValidationScheduler(); // 创建调取器,实际类型是ExecutorServiceSessionValidationScheduler
tSessionValidationScheduler(scheduler); // 将调度器绑定到ssionManager
}
// it is possible that that a scheduler was already created and t via 'tSessionValidationScheduler()'
// but would not have been enabled/started yet
if (!scheduler.isEnabled()) {
if (log.isInfoEnabled()) {
log.info("Enabling ssion ");
}
afterSessionValidationEnabled(); // 什么也没做,供继承,便于拓展
}
}
View Code
ExecutorServiceSessionValidationScheduler类图如下,它实现了Runnable接⼝
调⽤scheduler的enableSessionValidation(),enableSessionValidation⽅法如下
public void enableSessionValidation() {
if (this.interval > 0l) {
换算英文// 创建ScheduledExecutorService
this.rvice = wSingleThreadScheduledExecutor(new ThreadFactory() {
private final AtomicInteger count = new AtomicInteger(1);
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.tDaemon(true);
thread.tName(threadNamePrefix + AndIncrement());
return thread;
}
});
/
/ 初始化rvice interval时长之后开始执⾏this的run⽅法,每隔interval执⾏⼀次;注意interval的单位是TimeUnit.MILLISECONDS
this.rvice.scheduleAtFixedRate(this, interval, interval, TimeUnit.MILLISECONDS); // this就是ExecutorServiceSessionValidationScheduler⾃⼰
}
}
View Code
定时(默认每隔60分钟)的调⽤ExecutorServiceSessionValidationScheduler的run⽅法,run⽅法中调⽤ssionManager的validateSessions⽅法来完成ssion的验证,validateSessions⽅法如下
/
**
* @e ValidatingSessionManager#validateSessions()
*/
public void validateSessions() {
if (log.isInfoEnabled()) {
log.info("Validating all ");
}
int invalidCount = 0;
// 从ssionDao中获取全部的ssion
// ssionDao可以是默认的MemorySessionDAO,也可以是我们定制的CachingSessionDAO
Collection<Session> activeSessions = getActiveSessions();
if (activeSessions != null && !activeSessions.isEmpty()) {
// ⼀个⼀个校验
for (Session s : activeSessions) {
try {
//simulate a lookup key to satisfy the method signature.
//this could probably stand to be cleaned up in future versions:
SessionKey key = new Id());
validate(s, key); // 真正校验的⽅法
} catch (InvalidSessionException e) {
if (log.isDebugEnabled()) {
boolean expired = (e instanceof ExpiredSessionException);
String msg = "Invalidated ssion with id [" + s.getId() + "]" +
(expired ? " (expired)" : " (stopped)");
log.debug(msg);
}
invalidCount++; // 统计上次到这次定时任务间隔内过期的ssion个数
}
}
}
if (log.isInfoEnabled()) {
String msg = "Finished ssion validation.";
if (invalidCount > 0) {
msg += " [" + invalidCount + "] ssions were stopped.";
} el {
msg += " No ssions were stopped.";
}
log.info(msg);
}
}
View Code
validate⽅法如下
protected void validate(Session ssion, SessionKey key) throws InvalidSessionException {
try {
doValidate(ssion); // 真正校验ssion
} catch (ExpiredSessionException e) {
彪炳什么意思onExpiration(ssion, e, key); // 从ssionDao中删除过期的ssion
throw e; // 抛出异常供上层统计⽤
} catch (InvalidSessionException i) {
onInvalidation(ssion, i, key); // 从ssionDao中删除不合法的ssion
throw i; // 抛出异常供上层统计⽤
}
}
View Code
通过捕获doValidate()抛出的异常来剔除过期的或不合法的ssion,并将异常接着往上抛,供上层统计过期数量。注意:ExpiredSessionException的⽗类是StoppedSessionException,⽽StoppedSessionException的⽗类是InvalidSessionException。
doValidate⽅法如下
protected void doValidate(Session ssion) throws InvalidSessionException {
if (ssion instanceof ValidatingSession) {
((ValidatingSession) ssion).validate(); // 校验ssion是否过期
} el { // 若ssion不是ValidatingSession类型,则抛出IllegalStateException异常
String msg = "The " + getClass().getName() + " implementation only supports validating " +
"Session implementations of the " + Name() + " interface. " +
"Plea either implement this interface in your ssion implementation or override the " +
Name() + ".doValidate(Session) method to perform validation.";
throw new IllegalStateException(msg);
primer}
}
View Code
若ssion不是ValidatingSession类型,则抛出IllegalStateException异常
validate⽅法如下