package com.zheng.upms.client.shiro.session;
import com.zheng.common.util.RedisUtil;
import com.zheng.upms.client.util.SerializableUtil;
import com.zheng.upms.common.constant.UpmsConstant;
import org.apache.commons.lang.ObjectUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import java.io.Serializable;
import java.util.*;
/**
* 基于redis的sessionDao,缓存共享session
* Created by shuzheng on 2017/2/23.
*/
public class UpmsSessionDao extends CachingSessionDAO {
private static Logger _log = LoggerFactory.getLogger(UpmsSessionDao.class);
// 会话key
private final static String ZHENG_UPMS_SHIRO_SESSION_ID = "zheng-upms-shiro-session-id";
// 全局会话key
private final static String ZHENG_UPMS_SERVER_SESSION_ID = "zheng-upms-server-session-id";
// 全局会话列表key
private final static String ZHENG_UPMS_SERVER_SESSION_IDS = "zheng-upms-server-session-ids";
// code key
private final static String ZHENG_UPMS_SERVER_CODE = "zheng-upms-server-code";
// 局部会话key
private final static String ZHENG_UPMS_CLIENT_SESSION_ID = "zheng-upms-client-session-id";
// 单点同一个code所有局部会话key
private final static String ZHENG_UPMS_CLIENT_SESSION_IDS = "zheng-upms-client-session-ids";
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId, SerializableUtil.serialize(session), (int) session.getTimeout() / 1000);
_log.debug("doCreate >>>>> sessionId={}", sessionId);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
String session = RedisUtil.get(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId);
_log.debug("doReadSession >>>>> sessionId={}", sessionId);
return SerializableUtil.deserialize(session);
}
@Override
protected void doUpdate(Session session) {
// 如果会话过期/停止 没必要再更新了
if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid()) {
return;
}
// 更新session的最后一次访问时间
UpmsSession upmsSession = (UpmsSession) session;
UpmsSession cacheUpmsSession = (UpmsSession) doReadSession(session.getId());
if (null != cacheUpmsSession) {
upmsSession.setStatus(cacheUpmsSession.getStatus());
upmsSession.setAttribute("FORCE_LOGOUT", cacheUpmsSession.getAttribute("FORCE_LOGOUT"));
}
RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + session.getId(), SerializableUtil.serialize(session), (int) session.getTimeout() / 1000);
// 更新ZHENG_UPMS_SERVER_SESSION_ID、ZHENG_UPMS_SERVER_CODE过期时间 TODO
_log.debug("doUpdate >>>>> sessionId={}", session.getId());
}
@Override
protected void doDelete(Session session) {
String sessionId = session.getId().toString();
String upmsType = ObjectUtils.toString(session.getAttribute(UpmsConstant.UPMS_TYPE));
if ("client".equals(upmsType)) {
// 删除局部会话和同一code注册的局部会话
String code = RedisUtil.get(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId);
Jedis jedis = RedisUtil.getJedis();
jedis.del(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId);
jedis.srem(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code, sessionId);
jedis.close();
}
if ("server".equals(upmsType)) {
// 当前全局会话code
String code = RedisUtil.get(ZHENG_UPMS_SERVER_SESSION_ID + "_" + sessionId);
// 清除全局会话
RedisUtil.remove(ZHENG_UPMS_SERVER_SESSION_ID + "_" + sessionId);
// 清除code校验值
RedisUtil.remove(ZHENG_UPMS_SERVER_CODE + "_" + code);
// 清除所有局部会话
Jedis jedis = RedisUtil.getJedis();
Set<String> clientSessionIds = jedis.smembers(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code);
for (String clientSessionId : clientSessionIds) {
jedis.del(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + clientSessionId);
jedis.srem(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code, clientSessionId);
}
_log.debug("当前code={},对应的注册系统个数:{}个", code, jedis.scard(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code));
jedis.close();
// 维护会话id列表,提供会话分页管理
RedisUtil.lrem(ZHENG_UPMS_SERVER_SESSION_IDS, 1, sessionId);
}
// 删除session
RedisUtil.remove(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId);
_log.debug("doUpdate >>>>> sessionId={}", sessionId);
}
/**
* 获取会话列表
* @param offset
* @param limit
* @return
*/
public Map getActiveSessions(int offset, int limit) {
Map sessions = new HashMap();
Jedis jedis = RedisUtil.getJedis();
// 获取在线会话总数
long total = jedis.llen(ZHENG_UPMS_SERVER_SESSION_IDS);
// 获取当前页会话详情
List<String> ids = jedis.lrange(ZHENG_UPMS_SERVER_SESSION_IDS, offset, (offset + limit - 1));
List<Session> rows = new ArrayList<>();
for (String id : ids) {
String session = RedisUtil.get(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + id);
// 过滤redis过期session
if (null == session) {
RedisUtil.lrem(ZHENG_UPMS_SERVER_SESSION_IDS, 1, id);
total = total - 1;
continue;
}
rows.add(SerializableUtil.deserialize(session));
}
jedis.close();
sessions.put("total", total);
sessions.put("rows", rows);
return sessions;
}
/**
* 强制退出
* @param ids
* @return
*/
public int forceout(String ids) {
String[] sessionIds = ids.split(",");
for (String sessionId : sessionIds) {
// 会话增加强制退出属性标识,当此会话访问系统时,判断有该标识,则退出登录
String session = RedisUtil.get(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId);
UpmsSession upmsSession = (UpmsSession) SerializableUtil.deserialize(session);
upmsSession.setStatus(UpmsSession.OnlineStatus.force_logout);
upmsSession.setAttribute("FORCE_LOGOUT", "FORCE_LOGOUT");
RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId, SerializableUtil.serialize(upmsSession), (int) upmsSession.getTimeout() / 1000);
}
return sessionIds.length;
}
/**
* 更改在线状态
*
* @param sessionId
* @param onlineStatus
*/
public void updateStatus(Serializable sessionId, UpmsSession.OnlineStatus onlineStatus) {
UpmsSession session = (UpmsSession) doReadSession(sessionId);
if (null == session) {
return;
}
session.setStatus(onlineStatus);
RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + session.getId(), SerializableUtil.serialize(session), (int) session.getTimeout() / 1000);
}
}