Spring Boot session management with JWT

Viewed 5071

Can you assist me in this scenario? I am developing a mobile app where the session is not maintained at spring boot server side. Therefore I am using JWT which the client sends with every request.

The client app is sending data along with the token page by page (request by request) to the server. The Server needs to store this data temporary and waits for response data to arrive. It has to store all the data or nothing in the database.

Normally, with traditional web applications, this was possible through a session. I tried it with sessions, but it is not maintained. However, the session is maintained when requests come from Postman. The Client app runs on port 8000 whereas the server runs on SSL port 8443. One thing is clear, the server considers every request from the same client as anonymous although it does receives a token with each request.

SecurityConfigurer.java

@EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter{

    @Autowired
    private MyUserDetailsService userDetailsService;
    
    @Autowired
    private JwtRequestFilter jwtRequestFilter;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
        .cors().and()
        .authorizeRequests().antMatchers("/authenticate").permitAll()
        .anyRequest().authenticated()
        .and().sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
    
    

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        // TODO Auto-generated method stub
        return super.authenticationManagerBean();
    }
    
}



JwtRequestFilter.java

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        final String authorizationHeader = request.getHeader("Authorization");
        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = jwtUtil.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            if (jwtUtil.validateToken(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        
          response.setHeader("Access-Control-Allow-Origin", "*");
          response.setHeader("Access-Control-Allow-Credentials", "true");
          response.setHeader("Access-Control-Allow-Methods",
          "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age",
          "3600");
         

        filterChain.doFilter(request, response);
    }

}

QuizController.java

@CrossOrigin("*")
@RestController
public class QuizController {

    @Autowired
    private QuizRepository service;

    @Autowired
    private QuizSummaryRespository summaryService;

    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private MyUserDetailsService userDetailsService;
    
    @Autowired
    private JwtUtil jwtTokenUtil;

    @SuppressWarnings("unchecked")
    @ResponseBody
    @PostMapping("/quiz")
    public ResponseEntity<?> saveQuiz(Quiz quiz, @RequestParam String status, @RequestParam long time,
            HttpServletRequest request, HttpServletResponse response, @RequestHeader Map<String, String> headers) {
        

        Map<String, String> map = new HashMap<>();
        List<Quiz> myQuizzes = (List<Quiz>) request.getSession().getAttribute("code"); //This line always return null list

        if (quiz.getCode().equals("")) {
            quiz.setCode(Utility.generateCode());
            myQuizzes = new ArrayList<>();
        }

        myQuizzes.add(quiz);
        request.getSession().setAttribute("code", myQuizzes);

        map.put("code", quiz.getCode());

        return ResponseEntity.ok(map);
    }
}
1 Answers

Ok there are couple of ways that I can think of to solve this issue.

First approach

In frontend store all the previous response in sessionStorage or localStorage and send all at once when finished.

Second approach

In backend during first request store response with a unique id and send a unique id to the client. In each subsequent request, the client will need to send that unique id with the order and response. Once done get all the responses and merge them by order. You can use any type of store here either database, cache or plain array. Whatever meets your needs.

Related