spring-boot redis:如何使用户的所有会话无效?

2022-09-03 15:11:17

我是 redis 的新手。我按照本教程将 HttpSession 与 redis 结合使用。

https://docs.spring.io/spring-session/docs/current/reference/html5/guides/boot.html

现在我的应用程序具有“从所有设备注销”选项。单击该按钮后,如何删除该用户的所有会话或使其无效?

此外,当用户更改其密码时,我如何使他的所有会话(当前会话除外)无效?

编辑:

我尝试使用会话注册表。

@Autowired
private FindByIndexNameSessionRepository sessionRepository;

@Autowired
FindByIndexNameSessionRepository<? extends ExpiringSession> sessions;

@RequestMapping(value = "/logoutalldevices", method = RequestMethod.GET)
public Response test(HttpServletRequest request, HttpServletResponse response) throws Exception {

    SpringSessionBackedSessionRegistry sessionRegistry = new SpringSessionBackedSessionRegistry(sessionRepository);

    Collection<? extends ExpiringSession> usersSessions = sessions
            .findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "myUserId")
            .values();

    usersSessions.forEach((temp) -> {
        String sessionId = temp.getId();
        // sessionRegistry.removeSessionInformation(sessionId);
        SessionInformation info = sessionRegistry.getSessionInformation(sessionId);
        info.expireNow();
    });

    return Response.ok().build();
}

但它不会从 redis db 中删除会话或使其失效。尽管它正在向会话添加一个名为“sessionAttr:org.springframework.session.security.SpringSessionBackedSessionInformation.EXPIRED”的新属性,其值为 true。当我这样做时,我可以看到使用redis客户端在redis db中看到这个新的键值对

HGETALL 'sessionid'

编辑

我尝试使用redistemplate从redis db手动删除会话。

@Autowired
RedisTemplate<String, String> redisTemplate;

---------

redisTemplate.delete("spring:session:sessions:" + sessionId);
redisTemplate.delete("spring:session:sessions:expires:" + sessionId);

这几乎有效。它会从 redis db 中删除值,但不会删除键。

127.0.0.1:6379> keys *
1) "spring:session:sessions:25635a14-a4f1-4aa1-bf5a-bc20f972eec7"
2) "spring:session:sessions:expires:25635a14-a4f1-4aa1-bf5a-bc20f972eec7"
3) "spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:1"
127.0.0.1:6379> hgetall spring:session:sessions:25635a14-a4f1-4aa1-bf5a-bc20f972eec7
1) "lastAccessedTime"
2) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01[R'\x15\xc1"
127.0.0.1:6379> 

它删除了会话中除 lastAccessedTime 时间之外的所有其他键值对。

还有一个奇怪的是,这是我在执行时在redis监视器中看到的日志:redisTemplate.delete("key")

1491731944.899711 [0 127.0.0.1:62816] "DEL" "spring:session:sessions:25635a14-a4f1-4aa1-bf5a-bc20f972eec7"
1491731944.899853 [0 127.0.0.1:62816] "DEL" "spring:session:sessions:expires:25635a14-a4f1-4aa1-bf5a-bc20f972eec7"

如果我将上述两个命令复制并粘贴到 redis-client 并执行,则密钥将被删除。我执行时看不到键。我想知道为什么密钥在删除时使用时没有被删除keys *RedisTemplate

127.0.0.1:6379> "DEL" "spring:session:sessions:25635a14-a4f1-4aa1-bf5a-bc20f972eec7"
(integer) 1
127.0.0.1:6379> "DEL" "spring:session:sessions:expires:25635a14-a4f1-4aa1-bf5a-bc20f972eec7"
(integer) 1
127.0.0.1:6379> keys *
1) "spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:1"
127.0.0.1:6379>

答案 1

我想知道您正在遵循使用户会话无效的正确路径

    usersSessions.forEach((session) -> {        
        sessionRegistry.getSessionInformation(session.getId()).expireNow();
    });

需要注意的事项

SessionInformation.expireNow()

并不意味着从数据库中删除条目,它只是将过期的属性附加到会话中,正如您正确提到的。redis

但是,这如何使用户的会话无效呢?

这里出现了并发会话过滤器,其中方法可以执行以下技巧:.doFilter()automatically logging out

下面是 ConcurrentSessionFilter 的代码段

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    HttpSession session = request.getSession(false);

    if (session != null) {
        SessionInformation info = sessionRegistry.getSessionInformation(session
                .getId());

        if (info != null) {
            if (info.isExpired()) {
                // Expired - abort processing
                doLogout(request, response);

                String targetUrl = determineExpiredUrl(request, info);

                if (targetUrl != null) {
                    redirectStrategy.sendRedirect(request, response, targetUrl);

                    return;
                }
                else {
                    response.getWriter().print(
                            "This session has been expired (possibly due to multiple concurrent "
                                    + "logins being attempted as the same user).");
                    response.flushBuffer();
                }

                return;
            }
            else {
                // Non-expired - update last request date/time
                sessionRegistry.refreshLastRequest(info.getSessionId());
            }
        }
    }

    chain.doFilter(request, response);
}

为此干杯!


答案 2
@Autowired
private RedisIndexedSessionRepository redisIndexedSessionRepository;

redisIndexedSessionRepository.findByPrincipalName('your@login').keySet().forEach(redisIndexedSessionRepository::deleteById);

推荐