记一下Shiro重构之RedisSessionDAO

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;
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员朱永胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值