首页 > 解决方案 > 是否可以使用 jwt 身份验证进行自定义错误响应?

问题描述

我开始基于自定义错误响应构建我的项目,以便仅发送包含我需要的字段的 json 正文。出于这个原因,我有一个

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler

捕获所有异常并返回具有自定义错误主体的 ResponseEntity。

我有一个用于保存用户的 postgres 数据库。目前我有 /signin、/signup 和 /profile 端点。我想使用 jwt 身份验证。我使用了这个 github repo,当我在 /signin 端点上发送用户凭据时,我可以获得令牌。

这就是问题所在。阅读 JwtTokenFilter.java 的这一部分

  protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
    String token = jwtTokenProvider.resolveToken(httpServletRequest);
    try {
      if (token != null && jwtTokenProvider.validateToken(token)) {
        Authentication auth = jwtTokenProvider.getAuthentication(token);
        SecurityContextHolder.getContext().setAuthentication(auth);
      }
    } catch (CustomException ex) {
      //this is very important, since it guarantees the user is not authenticated at all
      SecurityContextHolder.clearContext();
      httpServletResponse.sendError(ex.getHttpStatus().value(), ex.getMessage());
      return;
    }

    filterChain.doFilter(httpServletRequest, httpServletResponse);
  }

假设我想注册一个新用户。然后我的请求头将没有令牌(令牌为空),程序将执行filterChain.doFilter(httpServletRequest, httpServletResponse);。这项工作很好,用户注册了,我得到了201我的控制器在成功注册后返回的信息。但是,假设我在 /profile 端点再次发出没有令牌的 GET 请求。这也将执行 filterChain.doFilter。然而,这一次 spring 将响应一个非自定义 403错误响应。我找不到在我的 RestControllerHandler 上捕获异常的方法,因为 spring 为我处理了它。

另外,当我在里面抛出异常时doFilterInternal,我的 GlobalHandler 不会再次处理异常,spring 会处理它。

标签: javaspringjwtresponsehandler

解决方案


将不得不添加自定义 AuthenticationFailureHandler

public class CustomAuthenticationFailureHandler 
  implements AuthenticationFailureHandler {

private ObjectMapper objectMapper = new ObjectMapper();

@Override
public void onAuthenticationFailure(
  HttpServletRequest request,
  HttpServletResponse response,
  AuthenticationException exception) 
  throws IOException, ServletException {

    response.setStatus(HttpStatus.UNAUTHORIZED.value());
    Map<String, Object> data = new HashMap<>();
    data.put(
      "timestamp", 
      Calendar.getInstance().getTime());
    data.put(
      "exception", 
      exception.getMessage());

    response.getOutputStream()
      .println(objectMapper.writeValueAsString(data));
 }
}

然后在这里配置

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Override
protected void configure(AuthenticationManagerBuilder auth) 
  throws Exception {
    auth.inMemoryAuthentication()
      .withUser("user1").password(passwordEncoder.encode("user1Pass")).roles("USER");
}

@Override
protected void configure(HttpSecurity http) 
  throws Exception {
    http
      .authorizeRequests()
      .anyRequest()
      .authenticated()
      .and()
      .formLogin()
      .failureHandler(authenticationFailureHandler());
}

@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
    return new CustomAuthenticationFailureHandler();
}
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
 }
}

推荐阅读