Spring HttpServletRequest 流关闭 IOException

Spring HttpServletRequest Stream closed IOException

我正在尝试为在移动应用程序上使用 Facebook 登录的用户生成 JWT 令牌。我将 facebook 数据发送到此函数:

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

然后我从我的 JWTAuthenticationFilter class 调用 attemptAuthentication:

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

}

但是 JWTAuthenticationFilter class 中的 attemptAuthentication 方法会抛出以下错误:java.io.IOException: Stream closed 在以下代码行中:

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

如果我通过 HTTP 请求调用 attemptAuthentication 方法,它工作正常。仅当从另一个 class 调用时,我才得到流关闭异常 是什么问题或可能是什么问题?

谢谢!

您使用

@RequestBody User user

对于第一种情况,因此 Spring 框架已经处理了 HttpServletRequest 实例的输入流(默认情况下只能发生一次 ),解析数据并注入user 实例到您的处理程序。

因此在调用处理程序时关闭输入流。您可以重载 attemptAuthentication 方法并将用户实例作为参数传递。

我想你没有任何 Spring 第二种情况的映射,所以输入流保持 "untouched" 并且你可以在身份验证方法中处理它。

我知道这个答案来得太晚了,但它是为将来可能遇到这个问题的人准备的。

如果您按照 udalmik 所说的进行了所有操作,但问题仍然存在,请仔细检查您是否将 UserDetailsS​​erviceImpl class 注释为 @Service。

是的,我花了 4 个小时才弄清楚我没有注释我的 class。