Shiro的会话方案一共三种
项目源码
如果使用会话的监听,共有两种方式可以监听会话,分别为实现会话监听器接口或者会话适配器中特定的方法覆盖。
为了方便会话监听测试,
进行两个监听器的设计
package cn.riversky.sessionlistener;
import org.apache.log4j.Logger;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;
/**
* 如果使用SessionListener接口的话那么就意味这需要对session生命周期进行监听
* @author riversky E-mail:[email protected]
* @version 创建时间 : 2018/1/27.
*/
public class MysessionListenerone implements SessionListener {
private static final Logger LOGGER=Logger.getLogger(MysessionListenerone.class);
private static int Online=0;
@Override
public void onStart(Session session) {
Online+=1;
LOGGER.info("创建会话"+session.getId()+",当前在线数量:"+Online);
}
@Override
public void onStop(Session session) {
Online-=1;
LOGGER.info("会话过期,当前在线数量:"+Online);
}
@Override
public void onExpiration(Session session) {
LOGGER.info("创建停止,当前在线数量:"+Online);
}
}
package cn.riversky.sessionlistener;
import org.apache.log4j.Logger;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListenerAdapter;
/**
* @author riversky E-mail:[email protected]
* @version 创建时间 : 2018/1/27.
*/
public class MysessionListenerTwo extends SessionListenerAdapter{
private static final Logger LOGGER=Logger.getLogger(MysessionListenerTwo.class);
@Override
public void onStart(Session session) {
LOGGER.info("SessionListenerAdapter:创建会话");
}
}
ini配置文件
[main]
;默认是/login.jsp
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized
perms.unauthorizedUrl=/unauthorized
logout.redirectUrl=/login
;session
cookie=org.apache.shiro.web.servlet.SimpleCookie
cookie.name=sid
cookie.maxAge=1800
cookie.httpOnly=true
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager.sessionIdCookie=$cookie
sessionManager.sessionIdCookieEnabled=true
lisener1= cn.riversky.sessionlistener.MysessionListenerone
lisener2= cn.riversky.sessionlistener.MysessionListenerTwo
sessionManager.sessionListeners=$lisener1,$lisener2
securityManager.sessionManager=$sessionManager
[filters]
myFilterOnce=cn.riversky.filter.OnceFilter
myFilterTwo= cn.riversky.filter.AdviceFilterPrePost
PathMatchingFIlterDemo= cn.riversky.filter.PathMatchingFilterDemo
myAccessFilter= cn.riversky.filter.MyAccessContrlFilter
[users]
zhang=123,admin
wang=123
[roles]
admin=user:*,menu:*
[urls]
;/**=myFilterOnce
/logout2=logout
/login=anon,myAccessFilter[hello,word]
/logout=anon,myFilterTwo
/unauthorized=anon,PathMatchingFIlterDemo[cinfig,hello,word]
/static/**=anon
/authenticated=authc
/role=authc,roles[admin]
/permission=authc,perms["user:create"]
关于会话的持久化,一般常常会采用到nosql数据库中进行存储这样可以做到session的共享。当然tomcat也具有session共享的功能。这里我们采取echache缓存+jdbc持久化的方案和redis缓存+jdbc持久化的方案两种方案进行测试
持久化类架构中的使用
源码将web.xml中的配置文件修改为sessionDao.ini
mysql数据库
drop table if exists sessions;
create table sessions (
id varchar(200),
session varchar(2000),
constraint pk_sessions primary key(id)
) charset=utf8 ENGINE=InnoDB;
持久化类的设计
package cn.riversky.sessiondao;
import cn.riversky.utils.JdbcTempleteUtils;
import cn.riversky.utils.SerializableUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.springframework.jdbc.core.JdbcTemplate;
import java.io.Serializable;
import java.util.List;
/**
* 继承cachingsessionDao后会先检查缓存,缓存没有的再来jdbc中进行查找,
* 这也是session共享的一种方案。
* @author riversky E-mail:[email protected]
* @version 创建时间 : 2018/1/27.
*/
public class JdbcSessionDao extends CachingSessionDAO{
private JdbcTemplate jdbcTemplate= JdbcTempleteUtils.getJdbcTemplate();
/**
* 更新持久化session,比如过期时间等的更新
* @param session
*/
@Override
protected void doUpdate(Session session) {
//如果会话过期或者停止,就没有必要更新了
if(session instanceof ValidatingSession&&!((ValidatingSession)session).isValid()){
return ;
}
String sql="update sessions set session=? where id=?";
jdbcTemplate.update(sql,SerializableUtils.serialize(session),session.getId());
}
@Override
protected void doDelete(Session session) {
String sql="delete from sessions where id=?";
jdbcTemplate.update(sql,session.getId());
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId=generateSessionId(session);
assignSessionId(session,sessionId);
String sql="insert into sessions(id,session) values(?,?)";
jdbcTemplate.update(sql,sessionId, SerializableUtils.serialize(session));
return session.getId();
}
@Override
protected Session doReadSession(Serializable serializable) {
String sql="select session from sessions where id=?";
List sessionStrList=jdbcTemplate.queryForList(sql,String.class,serializable);
if(sessionStrList.size()==0){
return null;
}
return SerializableUtils.deserialize(sessionStrList.get(0));
}
}
缓存ecache
<ehcache name="es">
<diskStore path="java.io.tmpdir"/>
<cache name="sessionCache"
maxEntriesLocalHeap="10000"
overflowToDisk="false"
eternal="false"
diskPersistent="false"
timeToLiveSeconds="0"
timeToIdleSeconds="0"
statistics="true"
/>
ehcache>
shiro配置文件
[main]
;默认是/login.jsp
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized
perms.unauthorizedUrl=/unauthorized
logout.redirectUrl=/login
;session
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
;持久化
sessionDao=cn.riversky.sessiondao.JdbcSessionDao
sessionDao.activeSessionsCacheName=sessionCache
sessionIdGenerator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator
sessionDao.sessionIdGenerator=$sessionIdGenerator
sessionManager.sessionDAO=$sessionDao
;缓存
cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile=classpath:ehcache.xml
securityManager.cacheManager = $cacheManager
;cookie
cookie=org.apache.shiro.web.servlet.SimpleCookie
cookie.name=sid
cookie.maxAge=1800
cookie.httpOnly=true
sessionManager.sessionIdCookie=$cookie
sessionManager.sessionIdCookieEnabled=true
lisener1= cn.riversky.sessionlistener.MysessionListenerone
lisener2= cn.riversky.sessionlistener.MysessionListenerTwo
sessionManager.sessionListeners=$lisener1,$lisener2
securityManager.sessionManager=$sessionManager
[filters]
myFilterOnce=cn.riversky.filter.OnceFilter
myFilterTwo= cn.riversky.filter.AdviceFilterPrePost
PathMatchingFIlterDemo= cn.riversky.filter.PathMatchingFilterDemo
myAccessFilter= cn.riversky.filter.MyAccessContrlFilter
[users]
zhang=123,admin
wang=123
[roles]
admin=user:*,menu:*
[urls]
;/**=myFilterOnce
/logout2=logout
/login=anon,myAccessFilter[hello,word]
/logout=anon,myFilterTwo
/unauthorized=anon,PathMatchingFIlterDemo[cinfig,hello,word]
/static/**=anon
/authenticated=authc
/role=authc,roles[admin]
/permission=authc,perms["user:create"]
测试
打开两个不同的浏览器,进行访问,看看数据库持久化中是否具有session
这里我们使用三方包方便我们缓存的实现
启动redis服务器后
配置文件修改成如下
引入redis缓存的三方依赖
<dependency>
<groupId>org.crazycakegroupId>
<artifactId>shiro-redisartifactId>
<version>2.4.2.1-RELEASEversion>
dependency>
修改配置文件
[main]
;默认是/login.jsp
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized
perms.unauthorizedUrl=/unauthorized
logout.redirectUrl=/login
;session
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
;cache
#===================================
# Redis Manager
#===================================
# Create redisManager
redisManager = org.crazycake.shiro.RedisManager
# Redis host. If you don't specify host the default value is 127.0.0.1 (Optional)
redisManager.host = localhost
# Redis port. Default value: 6379 (Optional)
redisManager.port = 6379
# Redis cache key/value expire time. Default value:0 .The expire time is in second (Optional)
redisManager.expire = 600
# Redis connect timeout. Timeout for jedis try to connect to redis server(In milliseconds).(Optional)
redisManager.timeout = 0
# Redis password.(Optional)
#redisManager.password =
# Redis database. Default value is 0(Optional)
#redisManager.database = 0
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
;cookie
cookie=org.apache.shiro.web.servlet.SimpleCookie
cookie.name=sid
cookie.maxAge=1800
cookie.httpOnly=true
sessionManager.sessionIdCookie=$cookie
sessionManager.sessionIdCookieEnabled=true
;缓存
cacheManager=org.crazycake.shiro.RedisCacheManager
cacheManager.keyPrefix = shiro:cache:
cacheManager.redisManager = $redisManager
securityManager.cacheManager = $cacheManager
lisener1= cn.riversky.sessionlistener.MysessionListenerone
lisener2= cn.riversky.sessionlistener.MysessionListenerTwo
sessionManager.sessionListeners=$lisener1,$lisener2
securityManager.sessionManager=$sessionManager
[filters]
myFilterOnce=cn.riversky.filter.OnceFilter
myFilterTwo= cn.riversky.filter.AdviceFilterPrePost
PathMatchingFIlterDemo= cn.riversky.filter.PathMatchingFilterDemo
myAccessFilter= cn.riversky.filter.MyAccessContrlFilter
[users]
zhang=123,admin
wang=123
[roles]
admin=user:*,menu:*
[urls]
;/**=myFilterOnce
/logout2=logout
/login=anon,myAccessFilter[hello,word]
/logout=anon,myFilterTwo
/unauthorized=anon,PathMatchingFIlterDemo[cinfig,hello,word]
/static/**=anon
/authenticated=authc
/role=authc,roles[admin]
/permission=authc,perms["user:create"]
修改web.xml中的ini环境配置,开启jetty服务
由于会话的验证是基于轮询的方式,采用固定频率的对数据进行会话验证是否过期。所以可以采用jdk自带的调度器ScheduledExecutorService,但是如果有特殊要求的话可能需要功能比较多的Quartz的调度器,两者一般在选取时需要在系统某个时刻,或者每天,每月,每年等固定的时间点执行的话就可以采取该Quartz的方式,这里直接使用Quartz进行测试。
<dependency>
<groupId>commons-collectionsgroupId>
<artifactId>commons-collectionsartifactId>
<version>3.2.1version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-quartzartifactId>
<version>1.2.2version>
<exclusions>
<exclusion>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
exclusion>
exclusions>
dependency>
主配置文件
[main]
;默认是/login.jsp
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized
perms.unauthorizedUrl=/unauthorized
logout.redirectUrl=/login
;session
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
lisener1= cn.riversky.sessionlistener.MysessionListenerone
lisener2= cn.riversky.sessionlistener.MysessionListenerTwo
sessionManager.sessionListeners=$lisener1,$lisener2
;持久化
sessionDao=cn.riversky.sessiondao.JdbcSessionDao
sessionDao.activeSessionsCacheName=sessionCache
sessionManager.sessionDAO=$sessionDao
cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile=classpath:ehcache.xml
securityManager.cacheManager = $cacheManager
sessionIdGenerator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator
sessionDao.sessionIdGenerator=$sessionIdGenerator
;cookie
cookie=org.apache.shiro.web.servlet.SimpleCookie
cookie.name=sid
cookie.maxAge=1800
cookie.httpOnly=true
sessionManager.sessionIdCookie=$cookie
sessionManager.sessionIdCookieEnabled=true
;会话管理
sessionManager.globalSessionTimeout=1800000
sessionManager.sessionValidationSchedulerEnabled=true
sessionValidationScheduler=org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler
sessionValidationScheduler.sessionValidationInterval = 10000
sessionValidationScheduler.sessionManager=$sessionManager
sessionManager.sessionValidationScheduler=$sessionValidationScheduler
sessionManager.deleteInvalidSessions=true
securityManager.sessionManager=$sessionManager
[filters]
myFilterOnce=cn.riversky.filter.OnceFilter
myFilterTwo= cn.riversky.filter.AdviceFilterPrePost
PathMatchingFIlterDemo= cn.riversky.filter.PathMatchingFilterDemo
myAccessFilter= cn.riversky.filter.MyAccessContrlFilter
[users]
zhang=123,admin
wang=123
[roles]
admin=user:*,menu:*
[urls]
;/**=myFilterOnce
/logout2=logout
/login=anon,myAccessFilter[hello,word]
/logout=anon,myFilterTwo
/unauthorized=anon,PathMatchingFIlterDemo[cinfig,hello,word]
/static/**=anon
/authenticated=authc
/role=authc,roles[admin]
/permission=authc,perms["user:create"]
测试
每隔固定周期执行,如果有特殊需求,可以