How do I look up an HttpSession by it's Id in a Springboot based application?

Viewed 19

I want to obtain a HttpSession object by URL Path variable id to get some attributes from it.

Context: I'm trying to implement a web server that has a register and login sub-systems as a learning exercise.

I'm using JAVA, Springboot and various other spring dependencies like hibernate, jdbc, etc.

I got the behavior I wanted, but as I tested my logic with an Android client application I encountered that the register confirmation link I send, does not work if I access it from another device, because the device-sender has a different session and thus my logic fails.

The flow of my registration is as follows:

  • User POSTs at /register -> { name, email, password }
  • Server saves this information in their session and sends confirmation email with /register/confirm/{token}
  • As the user GETs at /register/confirm/{token} that was send to their email, the server checks if this token is contained in their session and commits the information from the session to the database.

Of course if I register from the device and try to confirm through another device they'd have different sessions and hence the temp information would not be available to the other device, but this is the same user trying to register and I'm looking for a work around. The way I decided to change my code is to send the user /register/confirm/{sessionId}+{token} to their email, but I can't find my way around obtaining the other HttpSession.

(@ServletComponentScan) I tried to create a HttpSessionListener and tried to maintain a HashMap of HttpSession's but for some reason the Framework would instantiate the Listener object, but never send createSession events to it thus it's HashMap is always empty, thus {sessionId} is never found.

To provide some extra code for context.

My Listener:

@WebListener
public class SessionLookUpTable implements HttpSessionListener {
        static final HashMap<String, HttpSession> sessionHashMap = new HashMap<>();
        
        public SessionLookUpTable() {
            super();
            System.out.println("-------------- Session Listener Created"); // DEBUG
        }
// Always empty for some reason, despite constructor being called
        static public Optional<HttpSession> findSessionById(String sessionId) {
            if (!sessionHashMap.containsKey(sessionId))
                return Optional.empty();
            
            return Optional.of( sessionHashMap.get( sessionId ) );
        }
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            HttpSessionListener.super.sessionCreated(se);
            HttpSession session = se.getSession();

            sessionHashMap.put( session.getId(), session );
        }
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            HttpSessionListener.super.sessionDestroyed(se);
            sessionHashMap.remove(se.getSession().getId() );
        }
    };

The controller entry points

@PostMapping("/register")
    public String register(HttpSession session,
                           @RequestParam("email") String username,
                           @RequestParam("password") String password,
                           @RequestParam("password2") String pw2)
    {
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        user.setPrivilegeLevel( Role.USER_PRIVILEGE_NORMAL );

        if(session.getAttribute(ATTRIBUTE_USER_ID) != null) {
            return "Already registered";
        }

        if(!userService.isUserDataValid(user)) {
            return "Invalid input for registry";
        }

        if(userService.usernameExists(user.getUsername())) {
            return "User already exists";
        }

        session.setAttribute(ATTRIBUTE_REGISTER_DATA, user);

        String token = userService.sendConfirmationEmail( session );
        if(token != null) {
            session.setAttribute(ATTRIBUTE_USER_ID, 0L );
            session.setAttribute(ATTRIBUTE_REGISTER_TOKEN, token);
        }

        return "A link was sent to your email.";
    }

 @RequestMapping("/register/confirm/{sessionId}+{token}")
    void confirmRegister(HttpSession sessionIn,    
                         @PathVariable("sessionId") String sessionId,
                         @PathVariable("token") String token) {

        Optional<HttpSession> optSession = SessionLookUpTable.findSessionById( sessionId );

        if(optSession.isEmpty())
            return;

        HttpSession session = optSession.get();

        // Multiple confirmations guard
        Long userId = (Long)session.getAttribute(ATTRIBUTE_USER_ID);
        if( userId != null && userId != 0L ){
            return;
        }

        String sessionToken = (String)session.getAttribute(ATTRIBUTE_REGISTER_TOKEN);
        if(!sessionToken.equals(token)) {
            return;
        }

        User user = (User)session.getAttribute(ATTRIBUTE_REGISTER_DATA);
        user.setDateRegistered( LocalDate.now() );
        Long id = userService.register( user );

        session.setAttribute(ATTRIBUTE_USER_ID, id);
    }

I'm stuck at this stage for quite a while, so any help is appreciated. Thank you.

0 Answers
Related