会话方案

Shiro的会话方案一共三种

  1. 基本环境
  2. web环境servlet容器session方案
  3. web环境自定义session方案
    会话方案

会话监听器

项目源码
如果使用会话的监听,共有两种方式可以监听会话,分别为实现会话监听器接口或者会话适配器中特定的方法覆盖。
为了方便会话监听测试,

shiro中的session方案。

进行两个监听器的设计

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:riversky@126.com
 * @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:riversky@126.com
 * @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"]

测试
开启jetty后访问login目录
浏览器测试

会话的持久化

关于会话的持久化,一般常常会采用到nosql数据库中进行存储这样可以做到session的共享。当然tomcat也具有session共享的功能。这里我们采取echache缓存+jdbc持久化的方案和redis缓存+jdbc持久化的方案两种方案进行测试
持久化类架构中的使用
dao持久化

echache+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:riversky@126.com
 * @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<String> sessionStrList=jdbcTemplate.queryForList(sql,String.class,serializable);
        if(sessionStrList.size()==0){
            return null;
        }
        return SerializableUtils.deserialize(sessionStrList.get(0));
    }
}

缓存ecache

<?xml version="1.0" encoding="UTF-8"?>
<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服务器后
配置文件修改成如下
引入redis缓存的三方依赖

<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>2.4.2.1-RELEASE</version>
</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-collections</groupId>
                    <artifactId>commons-collections</artifactId>
                    <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-quartz</artifactId>
            <version>1.2.2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-core</artifactId>
                </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"]

测试
每隔固定周期执行,如果有特殊需求,可以

这里写图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐