Shiro之保存Session到数据库中-yellowcong

将Session统一存放到Mysql数据库中进行管理操作,这样我们就可以通过向操作数据库一样,对session进行操作和处理了。实现Session存储到数据库的大致步骤是,1、创建Session表;2、创建操作Session表的Mapper,3、创建继承EnterpriseCacheSessionDAO 的Dao,4、配置管理session的Dao到securityManager中,5、配置ecache配置。

源码地址

https://gitee.com/yellowcong/shior-dmeo/tree/master/test

环境架构

服务 版本
数据库 Mysql
缓存 ehcache
框架 Spring+SpringMVC+Mybatis

项目结构

Shiro之保存Session到数据库中-yellowcong_第1张图片

添加SessionDao

数据库表设计

数据表设计中,我们可以自己在我的基础上,进行字段的扩展

CREATE TABLE `sys_session` (
  `id` varchar(200) NOT NULL COMMENT 'Sessoin的id',
  `session` varchar(2000) DEFAULT NULL COMMENT 'Session的序列化对象',
  `username` varchar(32) DEFAULT NULL COMMENT '用户名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

Shiro之保存Session到数据库中-yellowcong_第2张图片

SessionMapper.xml



<mapper namespace="com.yellowcong.shiro.dao.SessionMapper">
  <resultMap id="Session" type="com.yellowcong.shiro.model.Session">
    <id column="id" jdbcType="VARCHAR" property="id" />
    <result column="session" jdbcType="VARCHAR" property="session" />
    <result column="username" jdbcType="VARCHAR" property="username" />
  resultMap>

  
  <insert id="insert" >
    <selectKey keyProperty="id" order="AFTER" resultType="java.lang.String">
      SELECT LAST_INSERT_ID()
    selectKey>
    insert into sys_session (id,session)
    values (#{id,jdbcType=VARCHAR},#{session,jdbcType=VARCHAR})
  insert>

  
  <delete id="delete" parameterType="java.lang.String">
    delete from sys_session where id = #{sessionid,jdbcType=VARCHAR}
  delete>

  
  <update id="update" >
    update sys_session set 
        session = #{session,jdbcType=VARCHAR} 
        <if test="username != null">
         , username = #{username,jdbcType=VARCHAR} 
        if>
    where id = #{id,jdbcType=VARCHAR} 
   update>

   
   <select id="load" parameterType="java.lang.String" resultMap="Session">
    select * from sys_session where id = #{sessionid,jdbcType=VARCHAR}
  select>

  
   <select id="loadByUserName" resultMap="Session">
    select * from sys_session where username = #{username,jdbcType=VARCHAR}
  select>


mapper>

SessionMapper.java

package com.yellowcong.shiro.dao;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.yellowcong.shiro.model.Session;

/**
 * 创建日期:2017/12/21
* 创建时间:8:44:22
* 创建用户:yellowcong
* 机能概要: */
public interface SessionMapper { /** * 创建日期:2017/12/21
* 创建时间:8:44:54
* 创建用户:yellowcong
* 机能概要:插入session * * @param session */
public int insert(@Param("id") String id,@Param("session") String session); /** * 创建日期:2017/12/21
* 创建时间:8:48:06
* 创建用户:yellowcong
* 机能概要:删除session * * @param session * @return */
public int delete(String sessionid); /** * * 创建日期:2017/12/21
* 创建时间:8:48:23
* 创建用户:yellowcong
* 机能概要:删除session * * @param session * @return */
public int update(@Param("id") String id,@Param("session") String session,@Param("username") String username); /** * 创建日期:2017/12/21
* 创建时间:8:49:13
* 创建用户:yellowcong
* 机能概要:通过sessionid来获取session数据 * * @param sessionid * @return */
public Session load(String sessionid); /** * 创建日期:2017/12/21
* 创建时间:11:52:02
* 创建用户:yellowcong
* 机能概要:根据用户名获取sesssion * @param username * @return */
public List loadByUserName(@Param("username") String username); }

创建SessionDao,管理session

SessionDao需要继承EnterpriseCacheSessionDAO ,实现里面的抽象方法,同时,自己还添加了一个根据用户名来获取Session对象的方法。这个里面直接操作Session存储到数据库。

对于插入用户名,需要在update sessiond的地方做处理,不然获取不到用户名

package com.yellowcong.shiro.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.springframework.beans.factory.annotation.Autowired;

import com.yellowcong.shiro.utils.SerializableUtils;

/**
 * 创建日期:2017/12/21
* 创建时间:8:31:04
* 创建用户:yellowcong
* 机能概要:用于Session的保存 */
public class SessionDao extends EnterpriseCacheSessionDAO { @Autowired private SessionMapper sessionMapper; public void delete(Session session) { //删除session this.sessionMapper.delete(session.getId().toString()); } public void update(Session session) throws UnknownSessionException { //当是ValidatingSession 无效的情况下,直接退出 if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid() ) { return ; } //检索到用户名 String username = String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY)); //序列化Session this.sessionMapper.update(session.getId().toString(), SerializableUtils.serializ(session),username); } @Override protected Serializable doCreate(Session session) { //生成session的id Serializable sessionId = generateSessionId(session); //给Session设定id assignSessionId(session, sessionId); //插入session 到数据库 this.sessionMapper.insert(session.getId().toString(), SerializableUtils.serializ(session)); return sessionId; } /** * 创建日期:2017/12/21
* 创建时间:13:56:15
* 创建用户:yellowcong
* 机能概要:通过名称来获取用户 Session * @param username * @return */
public List loadByUserName(String username) { //获取session的字符串 List dbSessions = this.sessionMapper.loadByUserName(username); //判断是否存在用户的情况 if(dbSessions == null || dbSessions.size() == 0) { return null; } List result = new ArrayList(); for(com.yellowcong.shiro.model.Session session:dbSessions) { //加载session数据 String sessionStr = session.getSession(); //将Session的数据串,转化为对象 result.add(SerializableUtils.deserializ(sessionStr)); } return result; } @Override protected Session doReadSession(Serializable sessionId) { //获取session的字符串 com.yellowcong.shiro.model.Session dbSession = this.sessionMapper.load(sessionId.toString()); if(dbSession == null) { return null; } //加载session数据 String sessionStr = dbSession.getSession(); return SerializableUtils.deserializ(sessionStr); } }

SerializableUtils

序列和反序列Session对象,只有将session对象序列化成字符串,才可以存储到Mysql上,不能直接存

package com.yellowcong.shiro.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Base64;

import org.apache.shiro.session.Session;

/**
 * 创建日期:2017/12/21
* 创建时间:9:21:25
* 创建用户:yellowcong
* 机能概要: */
public class SerializableUtils { /** * 创建日期:2017/12/21
* 创建时间:9:25:30
* 创建用户:yellowcong
* 机能概要:将Session序列化成String类型 * @param session * @return */
public static String serializ(Session session) { try { //ByteArrayOutputStream 用于存储序列化的Session对象 ByteArrayOutputStream bos = new ByteArrayOutputStream(); //将Object对象输出成byte数据 ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(session); //将字节码,编码成String类型数据 return Base64.getEncoder().encodeToString(bos.toByteArray()); } catch (Exception e) { throw new RuntimeException("序列化失败"); } } /** * 创建日期:2017/12/21
* 创建时间:9:26:19
* 创建用户:yellowcong
* 机能概要:将一个Session的字符串序列化成字符串,反序列化 * @param sessionStr * @return */
public static Session deserializ(String sessionStr) { try { //读取字节码表 ByteArrayInputStream bis = new ByteArrayInputStream(Base64.getDecoder().decode(sessionStr)); //将字节码反序列化成 对象 ObjectInputStream in = new ObjectInputStream(bis); Session session = (Session) in.readObject(); return session; } catch (Exception e) { throw new RuntimeException("反序列化失败"); } } }

配置spring-shiro.xml配置文件

注意这个sessionDao 里面配置了activeSessionsCacheName 这个属性,这个在ecache.xml里面必须也配置一个shiro-activeSessionCache节点,用于存激活的session,简单来讲,就是登录的用户。


     

         
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> 
    bean>


    
    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>

    
    <bean id="sessionDao" class="com.yellowcong.shiro.dao.SessionDao">
        
        <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
        
        <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
    bean>

               
    
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        
        <property name="globalSessionTimeout" value="1800000"/>

        <property name="deleteInvalidSessions" value="true"/>

        
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        
        <property name="sessionValidationScheduler" ref="sessionValidationScheduler" />
        
        <property name="sessionDAO" ref="sessionDao"/>

        
        <property name="sessionIdCookieEnabled" value="true"/>
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
    bean>

    
    <bean id="sessionValidationScheduler"
        class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
        <property name="sessionValidationInterval" value="1800000" />
        <property name="sessionManager" ref="sessionManager" />
    bean>

        
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
        
        <property name="cacheManager" ref="cacheManager" />  

                
        <property name="authenticator" ref="authenticator"/>

        
        <property name="realms">
            <list>
                
                <ref bean="sampleRealm1"/>
                <ref bean="sampleRealm2"/>
            list>
        property>

        
      <property name="sessionManager" ref="sessionManager"/> 
    bean> 

下面是完整配置

其中还有一部分是关于Shiro生命周期的,存储在了Spring-mvc中,因为生命周期配置在spring-shiro.xml中不生效


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <description>== Shiro Components ==description>

    
     

         
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> 
    bean>


    
    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>

    
    <bean id="sessionDao" class="com.yellowcong.shiro.dao.SessionDao">
        
        <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
        
        <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
    bean>

               
    
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        
        <property name="globalSessionTimeout" value="1800000"/>

        <property name="deleteInvalidSessions" value="true"/>

        
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        
        <property name="sessionValidationScheduler" ref="sessionValidationScheduler" />
        
        <property name="sessionDAO" ref="sessionDao"/>

        
        <property name="sessionIdCookieEnabled" value="true"/>
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
    bean>

    
    <bean id="sessionValidationScheduler"
        class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
        <property name="sessionValidationInterval" value="1800000" />
        <property name="sessionManager" ref="sessionManager" />
    bean>

        
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
        
        <property name="cacheManager" ref="cacheManager" />  

                
        <property name="authenticator" ref="authenticator"/>

        
        <property name="realms">
            <list>
                
                <ref bean="sampleRealm1"/>
                <ref bean="sampleRealm2"/>
            list>
        property>

        
      <property name="sessionManager" ref="sessionManager"/> 
    bean>  


    
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="sid"/>
        <property name="httpOnly" value="true"/>
        <property name="maxAge" value="-1"/>
    bean>

    
    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="authenticationStrategy" >
            
            
            <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"/>
        property>
    bean>

    
    <bean id="sampleRealm1" class=" com.yellowcong.shiro.realm.SampleRealm" >

        
        
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                
                <constructor-arg index="0" type="java.lang.String" value="MD5" />
                
                <property name="hashIterations" value="1"/>
            bean>
        property>
    bean>


    
    <bean id="sampleRealm2" class=" com.yellowcong.shiro.realm.SampleRealm2" >

        
        
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                
                <constructor-arg index="0" type="java.lang.String" value="SHA1" />
                
                <property name="hashIterations" value="1"/>
            bean>
        property>
    bean>

    
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
       <property name="securityManager" ref="securityManager" />  
       
       <property name="loginUrl" value="/user/login" />  
       
       <property name="successUrl" value="/user/list" />  
       
       <property name="unauthorizedUrl" value="/user/error" />  

       
       <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
   bean> 

   
   <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionsMapBuilder" factory-method="loadFilterChainDefinitions"/>

   
   <bean id="filterChainDefinitionsMapBuilder" class="com.yellowcong.shiro.filter.FilterChainDefinitionsMapBuilder" />
beans>

spring-mvc.xml的shiro配置

 
   <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

      
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"  
        depends-on="lifecycleBeanPostProcessor">  
        <property name="proxyTargetClass" value="true" />  
    bean>  

    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
        <property name="securityManager" ref="securityManager" />  
    bean>     

Shiro之保存Session到数据库中-yellowcong_第3张图片

配置ecache

这个地方,必须添加一个shiro-activeSessionCache 的配置,不然就缓存不到session的数据了。



    -- Sets the path to the directory where cache .data files are created.

         If the path is a Java System Property it is replaced by
         its value in the running VM.

         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - Default temp file path -->
    "java.io.tmpdir"/>

    name="authorizationCache"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    

    name="authenticationCache"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    

    name="shiro-activeSessionCache"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    

    --Default Cache configuration. These will applied to caches programmatically created through
        the CacheManager.

        The following attributes are required for defaultCache:

        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->
    "10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

    --Predefined caches.  Add your cache configuration settings here.
        If you do not have a configuration for your cache a WARNING will be issued when the
        CacheManager starts

        The following attributes are required for defaultCache:

        name              - Sets the name of the cache. This is used to identify the cache. It must be unique.
        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->

    -- Sample cache named sampleCache1
        This cache contains a maximum in memory of 10000 elements, and will expire
        an element if it is idle for more than 5 minutes and lives for more than
        10 minutes.

        If there are more than 10000 elements it will overflow to the
        disk cache, which in this configuration will go to wherever java.io.tmp is
        defined on your system. On a standard Linux system this will be /tmp"
        -->
  
    

    


测试登录

Shiro之保存Session到数据库中-yellowcong_第4张图片

参考文章

http://blog.csdn.net/qq_32347977/article/details/51084480
http://blog.csdn.net/lhacker/article/details/20444295
http://blog.csdn.net/lhacker/article/details/19340757

你可能感兴趣的:(shiro)