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 所说的进行了所有操作,但问题仍然存在,请仔细检查您是否将 UserDetailsServiceImpl class 注释为 @Service。
是的,我花了 4 个小时才弄清楚我没有注释我的 class。
我正在尝试为在移动应用程序上使用 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 所说的进行了所有操作,但问题仍然存在,请仔细检查您是否将 UserDetailsServiceImpl class 注释为 @Service。
是的,我花了 4 个小时才弄清楚我没有注释我的 class。