代码语言:javascript复制
package com.ccb.web.shiro;
import com.ccb.web.configs.CsRedisUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import projects.commons.utils.ValidateUtils;
import java.io.Serializable;
import java.util.*;
/**
* redisDAO
*
* @author zhuyongsheng
* @date 2019/8/12
*/
@Data
@Slf4j
public class RedisSessionDAO extends EnterpriseCacheSessionDAO {
@Autowired
CsRedisUtil redisUtil;
// session key 前缀
public static final String DEFAULT_SESSION_KEY_PREFIX = "shiro:session:";
// session key 账号前缀
public static final String DEFAULT_SESSION_KEY_MIDDLE = ":shiro:account:";
// session session超时时间 默认1秒
private static final long DEFAULT_SESSION_IN_MEMORY_TIMEOUT = 1000L;
// 超时时间
private static final int DEFAULT_EXPIRE = -2;
private static final int NO_EXPIRE = -1;
// 超时时间
private int expire = DEFAULT_EXPIRE;
// 1秒钟有多少毫秒,作为被除数参与运算使用
private static final int MILLISECONDS_IN_A_SECOND = 1000;
// 创建本地线程保存session
private static ThreadLocal sessionsInThread = new ThreadLocal();
/**
* 更新session
*
* @author zhuyongsheng
* @date 2019/8/15
*/
@Override
public void update(Session session) throws UnknownSessionException {
try {
//如果会话过期/停止 没必要再更新了
if (session instanceof ValidatingSession && !((ValidatingSession) session).isValid()) {
return;
}
Session redisSession = readRedisSession(getRedisSessionKey(session.getId()));
if (ValidateUtils.isNotNull(redisSession) && ValidateUtils.isNotNull(redisSession.getAttribute("kickout"))) {
session.setAttribute("kickout", true);
}
if (ValidateUtils.isNull(session.getAttribute("loginOperate"))) {
if (ValidateUtils.isNull(redisSession)) {
return;
}
} else {
session.removeAttribute("loginOperate");
}
this.saveSession((SimpleSession) session);
} catch (Exception e) {
log.warn("==update==更新session失败=={}=={}=={}", e.getMessage(),
session, TokenManager.getUserEmail());
}
}
/**
* 保存session
*
* @author zhuyongsheng
* @date 2019/8/12
*/
public void saveSession(SimpleSession session) throws UnknownSessionException {
//session非空校验
if (session == null || session.getId() == null) {
log.warn("==saveSession==session 或 session id 不存在!==");
throw new UnknownSessionException("session 或 session id 不存在!");
}
//删除首次添加到redis中的数据,因为未获取到用户账号,所以值为空
redisUtil.del(DEFAULT_SESSION_KEY_PREFIX session.getId() DEFAULT_SESSION_KEY_MIDDLE
"null");
// key组成:前缀 sessionID
String key = getRedisSessionKey(session.getId());
//正常情况下
if (expire == DEFAULT_EXPIRE) {
redisUtil.set(key, session, (int) (session.getTimeout() / MILLISECONDS_IN_A_SECOND));
return;
}
//超时时间不正常或未设置,打印警告日志
if (expire != NO_EXPIRE && expire * MILLISECONDS_IN_A_SECOND < session.getTimeout()) {
log.warn("==saveSession==Redis缓存时间: {} 少于Session缓存时间:{}==请更改相关配置!"
, expire * MILLISECONDS_IN_A_SECOND, session.getTimeout());
}
this.redisUtil.set(key, session, expire);
}
/**
* 删除session
*
* @author zhuyongsheng
* @date 2019/8/12
*/
@Override
public void delete(Session session) {
//session非空校验
if (session == null || session.getId() == null) {
log.warn("==delete==session 或 session id 不存在!==");
return;
}
//获取key
Set<String> keys = redisUtil.scan(DEFAULT_SESSION_KEY_PREFIX TokenManager.getSession().getId() '*');
try {
for (String key : keys) {
redisUtil.del(key);
}
} catch (Exception e) {
log.error("==delete==删除session失败 | session id:{} == {}", session.getId(), e.getMessage());
}
}
/**
* 获取所有存活的session
*
* @return java.util.Collection<org.apache.shiro.session.Session>
* @author zhuyongsheng
* @date 2019/8/15
*/
@Override
public Collection<Session> getActiveSessions() {
//构建返回对象
Set<Session> sessions = new HashSet<>();
//扫描keys
Set<String> keys = redisUtil.scan(DEFAULT_SESSION_KEY_PREFIX "*");
//获取session
if (keys != null && keys.size() > 0) {
for (String key : keys) {
Session session = this.readSession(key);
sessions.add(session);
}
}
return sessions;
}
/**
* 清除所有缓存
*
* @author zhuyongsheng
* @date 2019/8/15
*/
public void clearCache() {
Set<String> keys = redisUtil.scan(DEFAULT_SESSION_KEY_PREFIX "*");
redisUtil.del(keys);
}
/**
* 清楚指定缓存
*
* @author zhuyongsheng
* @date 2019/8/15
*/
public void clearCache(String sessionId) {
redisUtil.del(sessionId);
}
public void clearCacheWithEmail(String key) {
Set<String> scan = scan(getRedisSessionKey(key));
for (String sessionId : scan) {
redisUtil.del(sessionId);
}
}
/**
* 创建sessionId
*
* @return java.io.Serializable
* @author zhuyongsheng
* @date 2019/8/15
*/
@Override
protected Serializable doCreate(Session session) {
if (session == null) {
log.error("==doCreate==session不存在!");
throw new UnknownSessionException("session is null");
}
Serializable sessionId = generateSessionId(session);
this.assignSessionId(session, sessionId);
this.saveSession((SimpleSession) session);
return sessionId;
}
/**
* 获取指定key的value
* 默认读取本地缓存,若读取不到则读取redis缓存
*
* @return org.apache.shiro.session.Session
* @author zhuyongsheng
* @date 2019/8/15
*/
@Override
protected Session doReadSession(Serializable sessionId) {
if (sessionId == null) {
log.warn("==doReadSession==sessionId不存在!");
return null;
}
Session session = null;
//从本地线程中获取缓存
try {
session = getSessionFromThreadLocal(sessionId);
} catch (Exception e) {
log.warn("读取本地session失败!{}", e.getMessage());
}
if (session != null) {
return session;
}
try {
session = (Session) redisUtil.get((String) sessionId);
setSessionToThreadLocal(sessionId, session);
} catch (Exception e) {
log.error("==doReadSession==读取session失败,sessionId= {},{}", sessionId, e.getMessage());
}
return session;
}
protected Session readRedisSession(Serializable sessionId) {
if (sessionId == null) {
log.warn("==doReadSession==sessionId不存在!");
return null;
}
Session session = null;
try {
session = (Session) redisUtil.get((String) sessionId);
setSessionToThreadLocal(sessionId, session);
} catch (Exception e) {
log.error("==doReadSession==读取session失败,sessionId= {},{}", sessionId, e.getMessage());
}
return session;
}
public Set<String> scan(String key) {
return redisUtil.scan(key);
}
/**
* 设置本地缓存
*
* @return void
* @author zhuyongsheng
* @date 2019/8/15
*/
@SuppressWarnings("unchecked")
public void setSessionToThreadLocal(Serializable sessionId, Session session) {
Map<Serializable, SessionInMemory> sessionMap = (Map<Serializable, SessionInMemory>) sessionsInThread.get();
if (sessionMap == null) {
sessionMap = new HashMap<>();
sessionsInThread.set(sessionMap);
}
SessionInMemory sessionInMemory = new SessionInMemory();
sessionInMemory.setCreateTime(new Date());
sessionInMemory.setSession(session);
sessionMap.put(getRedisSessionKey(sessionId), sessionInMemory);
}
/**
* 获取本地缓存
*
* @return org.apache.shiro.session.Session
* @author zhuyongsheng
* @date 2019/8/15
*/
@SuppressWarnings("unchecked")
public Session getSessionFromThreadLocal(Serializable sessionId) {
Session session = null;
if (sessionsInThread.get() == null) {
return null;
}
Map<Serializable, SessionInMemory> sessionMap = (Map<Serializable, SessionInMemory>) sessionsInThread.get();
SessionInMemory sessionInMemory = sessionMap.get(sessionId);
if (sessionInMemory == null) {
return null;
}
Date now = new Date();
long duration = now.getTime() - sessionInMemory.getCreateTime().getTime();
if (duration < DEFAULT_SESSION_IN_MEMORY_TIMEOUT) {
session = sessionInMemory.getSession();
} else {
sessionMap.remove(sessionId);
}
return session;
}
/**
* 拼凑sessionId
*
* @return java.lang.String
* @author zhuyongsheng
* @date 2019/8/15
*/
public String getRedisSessionKey(Serializable sessionId) {
return DEFAULT_SESSION_KEY_PREFIX sessionId DEFAULT_SESSION_KEY_MIDDLE TokenManager.getUserEmail();
}
public String getRedisSessionKey(String email) {
return DEFAULT_SESSION_KEY_PREFIX "*" DEFAULT_SESSION_KEY_MIDDLE email;
}
}