Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spring-boot redis : How to invalidate all sessions of a user?

I'm new to redis. I've followed this tutorial to use HttpSession with redis.

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

Now my application has 'Sign out from all devices' option. When that is clicked how do I remove or invalidate all sessions of that user?

Also when the user changes his password, how do I invalidate all his sessions except the current session?

Edit:

I tried using Session Registry.

@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();
}

But it is not removing the session from redis db or invalidating it. though it's adding a new attribute to the session named 'sessionAttr:org.springframework.session.security.SpringSessionBackedSessionInformation.EXPIRED' with value true. I can see this new key value pair in redis db using redis client when I do

HGETALL 'sessionid'

Edit

I tried deleting the session manually from redis db using redistemplate.

@Autowired
RedisTemplate<String, String> redisTemplate;

---------

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

This almost works. It deletes the value from redis db, but not the key.

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> 

It deleted all other key values pairs inside the session except lastAccessedTime time.

Also one weird this is, this is the log I see in redis monitor when redisTemplate.delete("key") is executed:

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"

If I copy and paste the above two commands to redis-client and execute, the keys are deleted. I do not see keys when I execute keys * anymore. I wonder why the key is not getting deleted when its deleted using 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>
like image 406
Drunken Daddy Avatar asked Mar 29 '17 10:03

Drunken Daddy


4 Answers

I would like to know you that you are following the correct path for invalidating the user sessions

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

Somethings to note

SessionInformation.expireNow()

is not mean to remove entries from the redis database, it just appends the expired attribute to session as you rightly mentioned.

But how this invalidates the session of the user?

Here comes the ConcurrentSessionFilter into play where .doFilter() method does the trick of automatically logging out

Here is the snippet for 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);
}

Cheers to that!

like image 76
Arjun Avatar answered Oct 17 '22 12:10

Arjun


@Autowired
private RedisIndexedSessionRepository redisIndexedSessionRepository;

redisIndexedSessionRepository.findByPrincipalName('your@login').keySet().forEach(redisIndexedSessionRepository::deleteById);
like image 30
ImeQ Avatar answered Oct 17 '22 12:10

ImeQ


Try this

usersSessions.forEach((session) -> {        
        sessionRegistry.delete(session.getId());
  });
like image 1
Sagar U Avatar answered Oct 17 '22 11:10

Sagar U


If you just want to do one time thing in debugging process, you can just log into redis_cli and flush all Redis keys.

$ redis-cli
127.0.0.1:6379> KEYS *
1) "spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:bbb"
2) "spring:session:expirations:1558782600000"
3) "spring:session:expirations:1558783140000"
4) "spring:session:sessions:expires:953146bf-7300-4394-bbf0-bf606ff6b326"
5) "spring:session:expirations:1558782540000"
6) "spring:session:sessions:953146bf-7300-4394-bbf0-bf606ff6b326"
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> KEYS *
(empty list or set)
127.0.0.1:6379>
like image 1
ozooxo Avatar answered Oct 17 '22 11:10

ozooxo