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);
}
}