MockMvc 抛出内部异常而不是返回带有 4xx 状态码的响应
MockMvc throws internal exception instead of returning response with 4xx status code
我正在尝试使用 MockMvc 编写 JwtTokenVerifier 测试
当我尝试使用无效的 Auth header 请求一些 API 时:它没有返回带有 4xx 状态代码的响应,而是抛出一个内部异常(在我的例子中是 AuthException)。
我应该在测试中期待这个异常吗
或者我需要做些什么才能得到回应?
(值“”和“123”的测试成功,但“Bearer qewqweqweqwe”的测试失败并出现 AuthException(io.jsonwebtoken.MalformedJwtException:JWT 字符串必须恰好包含 2 个句点字符。找到:0))
测试:
@ParameterizedTest
@ValueSource(strings = {"", "123", "Bearer qewqweqweqwe"})
public void throwsClientErrorOnRequestWithInvalidAuthHeader(String headerValue) throws Exception {
String requestBody = asJsonString(new CustomPageRequest());
mockMvc.perform(
MockMvcRequestBuilders.post("/users/paged")
.header(jwtConfig.getAuthorizationHeader(), headerValue)
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().is4xxClientError());
}
JwtTokenVerifier 过滤器:
public class JwtTokenVerifier extends OncePerRequestFilter {
//DI
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader(jwtConfig.getAuthorizationHeader());
if (StringUtils.isEmpty(authHeader) || !authHeader.startsWith(jwtConfig.getTokenPrefix())) {
logger.warn("Invalid Authorization header - '" + authHeader + "'");
filterChain.doFilter(request, response);
return;
}
try {
Claims body = getTokenBodyFromAuthHeader(authHeader);
String username = body.getSubject();
AuthUser userDetails = userDetailsService.loadUserByUsername(username);
CustomTokenBasedAuthentication authentication = new CustomTokenBasedAuthentication(userDetails);
SecurityContextHolder.getContext().setAuthentication(authentication);
userContext.setCurrentPrincipal(authentication);
} catch (JwtException e) {
logger.error("During authentication (token verification) exception occurred", e);
throw new AuthException("auth error");
}
filterChain.doFilter(request, response);
}
...
}
ApiExceptionHandler:
@ControllerAdvice(basePackages = {"bac9h.demoapp"})
public class ApiExceptionsHandler {
...
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(AuthException.class)
@ResponseBody
public String onAuthException(AuthException e) {
return "401 error: " + e.getMessage();
}
}
看起来好像一切都已连接并正常工作,但是您的 JWT 解析器识别出您提供的 Bearer 令牌甚至不是 JWT 的有效格式,因此它抛出异常。
我建议以正确的格式 BUT 创建 JWT,这在您的应用程序上下文中没有意义来测试您要验证的行为。试试 jwt.io.
我意识到我的 apiExceptionHandler 不适用于过滤器
:
如 java servlet 规范所指定,过滤器始终在调用 Servlet 之前执行。现在 @ControllerAdvice 仅对在 DispatcherServlet 中执行的控制器有用。因此,使用 Filter 并期望调用 @ControllerAdvice 或在这种情况下调用 @ExceptionHandler 是不会发生的。
为了获得适当的响应,我需要手动提供异常处理程序
Testing Spring MVC @ExceptionHandler method with Spring MVC Test
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(statusController)
.setControllerAdvice(new ExceptionController())
.build();
我正在尝试使用 MockMvc 编写 JwtTokenVerifier 测试
当我尝试使用无效的 Auth header 请求一些 API 时:它没有返回带有 4xx 状态代码的响应,而是抛出一个内部异常(在我的例子中是 AuthException)。 我应该在测试中期待这个异常吗 或者我需要做些什么才能得到回应?
(值“”和“123”的测试成功,但“Bearer qewqweqweqwe”的测试失败并出现 AuthException(io.jsonwebtoken.MalformedJwtException:JWT 字符串必须恰好包含 2 个句点字符。找到:0))
测试:
@ParameterizedTest
@ValueSource(strings = {"", "123", "Bearer qewqweqweqwe"})
public void throwsClientErrorOnRequestWithInvalidAuthHeader(String headerValue) throws Exception {
String requestBody = asJsonString(new CustomPageRequest());
mockMvc.perform(
MockMvcRequestBuilders.post("/users/paged")
.header(jwtConfig.getAuthorizationHeader(), headerValue)
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().is4xxClientError());
}
JwtTokenVerifier 过滤器:
public class JwtTokenVerifier extends OncePerRequestFilter {
//DI
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader(jwtConfig.getAuthorizationHeader());
if (StringUtils.isEmpty(authHeader) || !authHeader.startsWith(jwtConfig.getTokenPrefix())) {
logger.warn("Invalid Authorization header - '" + authHeader + "'");
filterChain.doFilter(request, response);
return;
}
try {
Claims body = getTokenBodyFromAuthHeader(authHeader);
String username = body.getSubject();
AuthUser userDetails = userDetailsService.loadUserByUsername(username);
CustomTokenBasedAuthentication authentication = new CustomTokenBasedAuthentication(userDetails);
SecurityContextHolder.getContext().setAuthentication(authentication);
userContext.setCurrentPrincipal(authentication);
} catch (JwtException e) {
logger.error("During authentication (token verification) exception occurred", e);
throw new AuthException("auth error");
}
filterChain.doFilter(request, response);
}
...
}
ApiExceptionHandler:
@ControllerAdvice(basePackages = {"bac9h.demoapp"})
public class ApiExceptionsHandler {
...
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(AuthException.class)
@ResponseBody
public String onAuthException(AuthException e) {
return "401 error: " + e.getMessage();
}
}
看起来好像一切都已连接并正常工作,但是您的 JWT 解析器识别出您提供的 Bearer 令牌甚至不是 JWT 的有效格式,因此它抛出异常。
我建议以正确的格式 BUT 创建 JWT,这在您的应用程序上下文中没有意义来测试您要验证的行为。试试 jwt.io.
我意识到我的 apiExceptionHandler 不适用于过滤器
如 java servlet 规范所指定,过滤器始终在调用 Servlet 之前执行。现在 @ControllerAdvice 仅对在 DispatcherServlet 中执行的控制器有用。因此,使用 Filter 并期望调用 @ControllerAdvice 或在这种情况下调用 @ExceptionHandler 是不会发生的。
为了获得适当的响应,我需要手动提供异常处理程序
Testing Spring MVC @ExceptionHandler method with Spring MVC Test
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(statusController)
.setControllerAdvice(new ExceptionController())
.build();