Shiro自定义SessionDao,将session持久化到数据库

1.对应数据库实体类

这边用了mybatis-plus以及lombok框架

@Data
@TableName(value = "client_session",autoResultMap = true)
@NoArgsConstructor
public class ClientSession implements Serializable
{
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @TableField(value = "cookie")
    private String cookie;

    @TableField(value = "session")
    private String session;

    public static final String COL_ID = "id";

    public static final String COL_COOKIE = "cookie";

    public static final String COL_SESSION = "session";

    public ClientSession(String cookie, String session)
    {
        this.cookie = cookie;
        this.session = session;
    }

}

表结构,除了id,都是varchar类型的
在这里插入图片描述

2.造一个工具类来序列号session对象,因为session是一个接口对象,序列化成json比较麻烦

import org.apache.shiro.session.Session;

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


public class SerializableUtils {

    /**
     * 将Session序列化成String类型
     * @param session 需要被序列话的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("序列化失败");
        }
    }


    /**
     * 机能概要:将一个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("反序列化失败");
        }
    }
}

3.自定义SessionDao 已经打了注释,基本可以看明白,Service层需要自己写,在此就不赘述了


import com.htsst.bmapp.domain.ClientSession;
import com.htsst.bmapp.service.impl.ClientSessionService;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.io.Serializable;

/**
 * @program: bmapp
 * @description: sessiondao, 自定义session持久化操作,
 * 目前是把所有的已经通过认证的session存入到数据库
 * 请求数据库的次数可能会比较多,因为每当过来一条请求的时候,doUpdate方法都会被调用一次,原因是每次请求后session中的请求时间会被更改
 * @author: Chou.Day
 * @create: 2021-04-29 16:47
 **/
@Component
public class AppSessionDao extends EnterpriseCacheSessionDAO
{

    Logger logger = LoggerFactory.getLogger(AppSessionDao.class);
    @Lazy
    @Autowired
    ClientSessionService sessionService;


    @Override
    protected Serializable doCreate(Session session)
    {

        //如果doRead方法中没有读取到session则会进入这个方法来创建一个session
        Serializable cookie = super.doCreate(session);
        //以下注释如果打开的话会将所有的session都存放到数据库,包括没有认证的session
       /* ClientSession entity = new ClientSession(cookie.toString(), SerializableUtils.serializ(session));
        sessionService.save(entity);*/
        logger.info("创建Session" + cookie);
        return cookie;
    }

    @Override
    protected Session doReadSession(Serializable sessionId)
    {
        logger.info("读取session" + sessionId);
        Session memSession = null;
        try
        {
            memSession = super.doReadSession(sessionId);//先从内存中读取session
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        if (memSession == null) //如果内存中读取不到,则从数据库中读取session
        {
            ClientSession session = sessionService.getSession(sessionId.toString());
            if (session != null) //如果数据库中可以读取到,则返回数据库中读取到的session
                memSession = SerializableUtils.deserializ(session.getSession());
        }
        return memSession;
    }

    @Override
    protected void doUpdate(Session session)
    {
        ClientSession clientSession = sessionService.getSession(session.getId().toString());//1.先从数据库获取session
        if (clientSession != null) //2.如果可以获取到session则更新,这样会导致每次有请求过来都会更新,因为请求的时间不一致,session中的时间会随着请求的时间变化,这边后续可以改造一下
        {
            clientSession.setSession(SerializableUtils.serializ(session));
            sessionService.updateById(clientSession);
            logger.info("更新数据库中的session:" + clientSession.getCookie());
        } else //3.如果获取不到session,并且session已经通过认证,则将session保存到数据库
        {
            Object obj = session.getAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
            if (obj != null)
            {
                String attribute = obj.toString();
                if (Boolean.parseBoolean(attribute))
                {
                    logger.info("已经认证session,存入数据库!" + session.getId());
                    ClientSession entity = new ClientSession(session.getId().toString(), SerializableUtils.serializ(session));
                    sessionService.save(entity);
                }
            }
        }
        super.doUpdate(session);
    }

    @Override
    protected void doDelete(Session session)
    {
        sessionService.deleteSession(session);
        super.doDelete(session);
    }
}

4.将自定义的sessionDao配置到shiro中,shiroConfig类中创建对象,并设置到sessionManager中,不要忘了把sessionmanager设置到DefaultWebSecurityManager中

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值