3

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);
    }
}
Abdullah Khan
  • 12,010
  • 6
  • 65
  • 78
Karar Haider
  • 31
  • 1
  • 2
  • So in your case user will submit response in steps and server needs to save it all. Is that correct I am just trying to understand your question. – nicholasnet Apr 12 '20 at 23:04
  • Thanks mate for your reply. Yes your understanding is correct. Request is coming from an app and server has no clue which request from client is relevant to previous request from the same client. No cookies or session is maintained. Only thing that client sends with each request is a unique token which gets generated by the server. The server generates the token only first time for each client. On subsequent requests, the client sends this token. Is there a way to create a Spring session based on these unique tokens? And then each time get access to same session based on this token. – Karar Haider Apr 13 '20 at 13:55

1 Answers1

3

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.

nicholasnet
  • 2,117
  • 2
  • 24
  • 46
  • In second approach, you mean to store **request** with a unique id right? That should be possible. Just to clarify myself, there is no way that we can create our own session object based on a unique id ? Each time, when we get unique id from client request, we get that session object from memory and add more data to it. I am also getting a hint now, "probably concept of session in this scenario may not applicable". – Karar Haider Apr 13 '20 at 18:02
  • There is no session involved in second or first approach. In second approach when you receive request for the first time generate unique id `UUID.randomUUID().toString()` (or some other id generator) then use id to track responses by order. – nicholasnet Apr 13 '20 at 19:12
  • Hi nicholasnet, I implemented second approach and it worked fine for me. Thanks ! – Karar Haider Apr 23 '20 at 22:23
  • I tried to accept but it does not allow me, probably my score level is not that much :-( – Karar Haider Apr 30 '20 at 23:07