skip to Main Content

I am trying to generate a JWT token for a user who has logged in with Facebook on the mobile app. I send the facebook data to this function:

@PostMapping("/facebook")
public void registerWithFacebook(@RequestBody User user, HttpServletRequest request, HttpServletResponse response){

    User loggingInUser = this.userRepository.findByUsername(user.getUsername());

    if( loggingInUser == null){
        user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        user.setBlocked(false);

        this.userRepository.save(user);
    }

    //Generate JWT token for user
    JWTAuthenticationFilter jwtAuthenticationFilter = new JWTAuthenticationFilter(this.authenticationManager, this.userRepository);
    jwtAuthenticationFilter.attemptAuthentication(request, response);
}

And I call the attemptAuthentication from my JWTAuthenticationFilter class:

public class JWTAuthenticationFilter extends 
UsernamePasswordAuthenticationFilter {

private AuthenticationManager authenticationManager;
private UserRepository userRepository;

public JWTAuthenticationFilter() {
}

public JWTAuthenticationFilter(AuthenticationManager authenticationManager, UserRepository userRepository) {
    this.authenticationManager = authenticationManager;
    this.userRepository = userRepository;
    setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/login", "POST"));
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException {
    try {
        User creds = new ObjectMapper()
                .readValue(request.getInputStream(), User.class);

        return authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        creds.getUsername(),
                        creds.getPassword(),
                        new ArrayList<>())
        );
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

@Override
protected void successfulAuthentication(HttpServletRequest request,
                                        HttpServletResponse response,
                                        FilterChain chain,
                                        Authentication authResult) throws IOException, ServletException {
    String token = Jwts.builder()
            .setSubject(((org.springframework.security.core.userdetails.User) authResult.getPrincipal()).getUsername())
            .setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
            .signWith(SignatureAlgorithm.HS512, SecurityConstants.JWT_SECRET.getBytes())
            .compact();
    response.addHeader(SecurityConstants.HEADER_STRING, token);

    String username = JWTAuthorizationFilter.getUsernameFromToken(token);
    User user = this.userRepository.findByUsername(username);

    Map<String, String> responseBody = new HashMap<>();
    responseBody.put("token", token);
    responseBody.put("blocked", Boolean.toString(user.isBlocked()));
    responseBody.put("paid", Boolean.toString(user.isPaid()));
    responseBody.put("endSubscription", user.getEndSubscribtionDate() + "");

    String jsonResponse = new ObjectMapper().writeValueAsString(responseBody);
    response.getWriter().write(jsonResponse);
}

}

But the attemptAuthentication method from JWTAuthenticationFilter class throws the following error: java.io.IOException: Stream closed in the following line of code :

 User creds = new ObjectMapper()
                .readValue(request.getInputStream(), User.class);

If I call the attemptAuthentication method through HTTP request, it works fine. What is or may be the problem that I get the stream closed exception ONLY when it is called from another class?

Thank you!

2

Answers


  1. You use

    @RequestBody User user
    

    for first case, so Spring framework already processed input stream of HttpServletRequest instance (can happen only once by default), parsed data and injected user instance to your handler.

    So input stream is closed when handler is called. You can overload attemptAuthentication method and pass user instance as a param instead.

    I suppose you don’t have any Spring mappings for second case, so input stream stays “untouched” and you are able to process it within the authentication method.

    Login or Signup to reply.
  2. I know this answer is super late but its for people that might have this problem in the future.

    If you did everything that udalmik said and you still have the problem double check that you annotated your UserDetailsServiceImpl class as a @Service.

    yep it took me 4 hours to figure that out I did not annotate my class.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search