前言:我们有时会有这样的需求,将Java的复杂对象存入数据库,并且在需要的时候解析出来,其实解决方案有很多,但是比较简单又好用的却不多,下面先说一下我的具体需求
需求:
在做 session 共享的时候,需要将 session 持久化的数据库中,每次有请求时候会从数据库查询出 session 数据,下面看一下 session 类。
我用的是 Shiro 的 SimpleSession ,看一下 SimpleSession 实现:
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package org.apache.shiro.session.mgt;
public class SimpleSession implements org.apache.shiro.session.mgt.ValidatingSession, java.io.Serializable {
private static final long serialVersionUID = -7125642695178165650L;
private static final transient org.slf4j.Logger log;
protected static final long MILLIS_PER_SECOND = 1000L;
protected static final long MILLIS_PER_MINUTE = 60000L;
protected static final long MILLIS_PER_HOUR = 3600000L;
static int bitIndexCounter;
private static final int ID_BIT_MASK;
private static final int START_TIMESTAMP_BIT_MASK;
private static final int STOP_TIMESTAMP_BIT_MASK;
private static final int LAST_ACCESS_TIME_BIT_MASK;
private static final int TIMEOUT_BIT_MASK;
private static final int EXPIRED_BIT_MASK;
private static final int HOST_BIT_MASK;
private static final int ATTRIBUTES_BIT_MASK;
private transient java.io.Serializable id;
private transient java.util.Date startTimestamp;
private transient java.util.Date stopTimestamp;
private transient java.util.Date lastAccessTime;
private transient long timeout;
private transient boolean expired;
private transient java.lang.String host;
private transient java.util.Map<java.lang.Object,java.lang.Object> attributes;
public SimpleSession() { /* compiled code */ }
public SimpleSession(java.lang.String host) { /* compiled code */ }
public java.io.Serializable getId() { /* compiled code */ }
public void setId(java.io.Serializable id) { /* compiled code */ }
public java.util.Date getStartTimestamp() { /* compiled code */ }
public void setStartTimestamp(java.util.Date startTimestamp) { /* compiled code */ }
public java.util.Date getStopTimestamp() { /* compiled code */ }
public void setStopTimestamp(java.util.Date stopTimestamp) { /* compiled code */ }
public java.util.Date getLastAccessTime() { /* compiled code */ }
public void setLastAccessTime(java.util.Date lastAccessTime) { /* compiled code */ }
public boolean isExpired() { /* compiled code */ }
public void setExpired(boolean expired) { /* compiled code */ }
public long getTimeout() { /* compiled code */ }
public void setTimeout(long timeout) { /* compiled code */ }
public java.lang.String getHost() { /* compiled code */ }
public void setHost(java.lang.String host) { /* compiled code */ }
public java.util.Map<java.lang.Object,java.lang.Object> getAttributes() { /* compiled code */ }
public void setAttributes(java.util.Map<java.lang.Object,java.lang.Object> attributes) { /* compiled code */ }
public void touch() { /* compiled code */ }
public void stop() { /* compiled code */ }
protected boolean isStopped() { /* compiled code */ }
protected void expire() { /* compiled code */ }
public boolean isValid() { /* compiled code */ }
protected boolean isTimedOut() { /* compiled code */ }
public void validate() throws org.apache.shiro.session.InvalidSessionException { /* compiled code */ }
private java.util.Map<java.lang.Object,java.lang.Object> getAttributesLazy() { /* compiled code */ }
public java.util.Collection<java.lang.Object> getAttributeKeys() throws org.apache.shiro.session.InvalidSessionException { /* compiled code */ }
public java.lang.Object getAttribute(java.lang.Object key) { /* compiled code */ }
public void setAttribute(java.lang.Object key, java.lang.Object value) { /* compiled code */ }
public java.lang.Object removeAttribute(java.lang.Object key) { /* compiled code */ }
public boolean equals(java.lang.Object obj) { /* compiled code */ }
protected boolean onEquals(org.apache.shiro.session.mgt.SimpleSession ss) { /* compiled code */ }
public int hashCode() { /* compiled code */ }
public java.lang.String toString() { /* compiled code */ }
private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { /* compiled code */ }
private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { /* compiled code */ }
private short getAlteredFieldsBitMask() { /* compiled code */ }
private static boolean isFieldPresent(short bitMask, int fieldBitMask) { /* compiled code */ }
}
由代码可以看到,SimpleSession 实现了和 Serializable,下面再看一下 ValidatingSession 接口
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package org.apache.shiro.session.mgt;
public interface ValidatingSession extends org.apache.shiro.session.Session {
boolean isValid();
void validate() throws org.apache.shiro.session.InvalidSessionException;
}
可以看到,ValidatingSession 接口继承了 org.apache.shiro.session.Session 接口,下面再看看 org.apache.shiro.session.Session 接口
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package org.apache.shiro.session;
public interface Session {
java.io.Serializable getId();
java.util.Date getStartTimestamp();
java.util.Date getLastAccessTime();
long getTimeout() throws org.apache.shiro.session.InvalidSessionException;
void setTimeout(long l) throws org.apache.shiro.session.InvalidSessionException;
java.lang.String getHost();
void touch() throws org.apache.shiro.session.InvalidSessionException;
void stop() throws org.apache.shiro.session.InvalidSessionException;
java.util.Collection<java.lang.Object> getAttributeKeys() throws org.apache.shiro.session.InvalidSessionException;
java.lang.Object getAttribute(java.lang.Object o) throws org.apache.shiro.session.InvalidSessionException;
void setAttribute(java.lang.Object o, java.lang.Object o1) throws org.apache.shiro.session.InvalidSessionException;
java.lang.Object removeAttribute(java.lang.Object o) throws org.apache.shiro.session.InvalidSessionException;
}
有上述代码可以看到,SimpleSession 对象是一个比较复杂的对象,既有很多个属性,又有多个复杂方法,如果直接使用Jackson工具转化为 json 格式字符串存入数据库,最终是无法解析出来的
我的解决方案是:
- 存储方案
- 1.1 将对象序列化到内存中,并生成一个字节数据
- 1.2 将1.1中得到的字节数组转化为 base64 位的字符串
- 1.3 将1.2中得到的字符串存入数据库即可
- 读取方案
- 2.1 从数据库中读取 base64 加密后的字符串
- 2.2 将2.1中的加密字符串解密成字节数组
- 2.3 将2.3中的字节数组反序列化
具体实现
我这里借助 org.apache.commons.lang3.SerializationUtils 工具类来实现,序列化功能代码如下:
package com.lixiaohao.test.springshiro.util;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.Session;
import java.io.Serializable;
/**
* @program: shiroTest
* @description:
* @author: xiaohao.li
* @create: 2018-07-30 14:55
**/
public class SerializeUtils extends SerializationUtils {
public static String serializeToString(Serializable obj) {
try {
byte[] value = serialize(obj);
return Base64.encodeToString(value);
} catch (Exception e) {
throw new RuntimeException("serialize session error", e);
}
}
public static Session deserializeFromString(String base64) {
try {
byte[] objectData = Base64.decode(base64);
return deserialize(objectData);
} catch (Exception e) {
throw new RuntimeException("deserialize session error", e);
}
}
}
session管理功能如下:
package com.lixiaohao.test.springshiro.session;
import com.lixiaohao.test.springshiro.dao.SessionModelDao;
import com.lixiaohao.test.springshiro.dao.UserDao;
import com.lixiaohao.test.springshiro.model.SessionModel;
import com.lixiaohao.test.springshiro.model.User;
import com.lixiaohao.test.springshiro.util.JsonUtils;
import com.lixiaohao.test.springshiro.util.SerializeUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.Serializable;
import com.lixiaohao.test.springshiro.util.StringUtils;
/**
* @program: shiroTest
* @description:
* @author: xiaohao.li
* @create: 2018-07-26 12:40
**/
public class UserCacheSessionDao extends CachingSessionDAO{
private final String PRINCIPALS_SESSION_KEY = "org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY";
@Autowired
private SessionModelDao sessionModelDao;
@Autowired
private UserDao userDao;
protected void doUpdate(Session session) {
}
protected void doDelete(Session session) {
if ( session == null ) {
throw new NullPointerException(" session argument can not be null ");
}
String sessionId = (String)session.getId();
if ( StringUtils.isEmpty(sessionId) ) {
throw new NullPointerException(" property sessionId can not be null in session argument ");
}
sessionModelDao.delete(sessionId);
}
protected Serializable doCreate(Session session) {
if ( session == null ) {
throw new NullPointerException(" session argument can not be null ");
}
String sessionId = (String) generateSessionId(session);
assignSessionId(session,sessionId);
storeSession(session);
return sessionId;
}
private void storeSession(Session session){
String sessionId = (String) session.getId();
SessionModel sessionModel = new SessionModel();
sessionModel.setSessionId(sessionId);
sessionModel.setStatus(1);
//将对象序列化
String sessionStr = SerializeUtils.serializeToString((SimpleSession)session);
sessionModel.setSessionStr(sessionStr);
Object principals = session.getAttribute(PRINCIPALS_SESSION_KEY);
if ( principals != null ) {
User user = userDao.findByUserName((String) principals);
sessionModel.setValue(JsonUtils.objectToJson(user));
}
System.out.println("保存session -------->> "+JsonUtils.objectToJson(sessionModel));
sessionModelDao.insert(sessionModel);
}
protected Session doReadSession(Serializable sessionId) {
if ( sessionId == null ) {
throw new NullPointerException(" sessionId argument can not be null. ");
}
String id = (String) sessionId;
SessionModel sessionMode = sessionModelDao.findBySessionId(id);
if ( sessionMode == null ) {
return null;
}
java.lang.String sessionStr = sessionMode.getSessionStr();
//反序列化对象
Session session = SerializeUtils.deserializeFromString(sessionStr);
return session;
}
}
本文暂时没有评论,来添加一个吧(●'◡'●)