[英]Spring: REST exceptions are caught as 403 Forbidden exception
[英]Spring REST Api respond with empty body + 403 Forbidden on runtime exceptions
我创建了一个 Spring 引导 Rest Api 和自定义 Z1D1FADBD9150349C1357811140 身份验证。 我的问题是,例如,当我发送带有过期或无效 JWT 令牌的请求时,我收到如下异常:
com.auth0.jwt.exceptions.SignatureVerificationException: The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA512
这显然没问题,但是响应正文是空的,因此客户端不知道为什么会出现 403 错误。
问题与 Spring 的 BadCredentials Exception 等相同......
如何将这些异常转换为自定义错误响应而不是“403 禁止”?
Spring Web 配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsServiceImpl userDetailsService;
@Autowired
public WebSecurityConfig(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManager());
filter.setFilterProcessesUrl(AUTH_URL);
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(filter)
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
JWTAuthenticationFilter
private final AuthenticationManager authenticationManager;
@Autowired
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
String decoded = new String(Base64.getDecoder().decode(new String(req.getInputStream().readAllBytes())));
AuthenticationDetails details = new Gson().fromJson(decoded, AuthenticationDetails.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
details.getUsername(),
details.getPassword(),
new ArrayList<>()));
} catch (TokenExpiredException e) {
req.setAttribute("expired", e.getMessage());
throw new TokenExpiredException(e.getMessage());
} catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = JWT.create()
.withSubject(((User) auth.getPrincipal()).getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(Algorithm.HMAC512(SECRET.getBytes()));
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
JWTAuthorizationFilter
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
如果您从AbstractAuthenticationProcessingFilter
扩展JWTAuthenticationFilter
,您可以覆盖unsuccessfulAuthentication
,如下所示:
@Override
protected void unsuccessfulAuthentication(
HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
SecurityContextHolder.clearContext();
failureHandler.onAuthenticationFailure(request, response, failed);
}
现在,如您所见,我已将故障处理委托给我的failureHandler
,它的类型为org.springframework.security.web.authentication.AuthenticationFailureHandler
。
为此,您需要注册您的自定义故障处理程序。 您可以通过从org.springframework.security.web.authentication.AuthenticationFailureHandler
实现您的处理程序并覆盖onAuthenticationFailure
,并检查从JWTAuthenticationFilter
抛出的异常实例,如下所示:
@Component
public class MyAuthFailureHandler implements AuthenticationFailureHandler {
private final ObjectMapper mapper;
@Autowired
public MyAuthFailureHandler(ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
public void onAuthenticationFailure(
HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
throws IOException, ServletException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
if (e instanceof BadCredentialsException) {
mapper.writeValue(
response.getWriter(),
ErrorResponse.of(
"Invalid username or password", ErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
} else if (e instanceof JwtExpiredTokenException) {
mapper.writeValue(
response.getWriter(),
ErrorResponse.of(
"Token has expired", ErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED));
} else if (e instanceof AuthMethodNotSupportedException) {
mapper.writeValue(
response.getWriter(),
ErrorResponse.of(e.getMessage(), ErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
} else if (e instanceof TokenEncryptionException) {
mapper.writeValue(
response.getWriter(),
ErrorResponse.of(e.getMessage(), ErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
} else if (e instanceof InvalidJwtAuthenticationTokenException) {
mapper.writeValue(
response.getWriter(),
ErrorResponse.of(e.getMessage(), ErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
}
mapper.writeValue(
response.getWriter(),
ErrorResponse.of(
"Authentication failed", ErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.