java - 是否可以使用 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 会处理它。
解决方案
将不得不添加自定义 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();
}
}
推荐阅读
- node.js - 找不到模块“验证器”
- powershell - 从 git 运行 PS1 脚本的 Jenkinsfile 管道
- c - 出现错误:错误的文件描述符:***检测到堆栈粉碎***在写入客户端套接字时,但套接字描述符是正确的
- php - 将firebase时间戳转换为php日期
- c# - 算法正确性的证明
- python - 使用 Redis 在 C++ 和 python 之间共享 Opencv 图像
- sql - 使用 json 格式的 array_agg 检索一对多关系行 - postgresql
- android - 如何使用 espresso 执行按下多个按键事件
- python - Python:合并具有不同标题的csv数据
- android - 有没有办法在弹出窗口中创建 viewpager?