英文:
MockMvc throws internal exception instead of returning response with 4xx status code
问题
以下是你提供的内容的翻译部分:
我正在尝试使用MockMvc编写JwtTokenVerifier测试。
当我尝试使用无效的Auth标头请求某些API时:与其返回带有4xx状态代码的响应,它会抛出内部异常(在我这里是AuthException)。
在测试中,我应该期望这个异常吗?
还是我需要做些什么来获得响应?
(对于值“”和“123”,测试成功,但对于“Bearer qewqweqweqwe”,由于AuthException而失败
(io.jsonwebtoken.MalformedJwtException: JWT字符串必须恰好包含2个句点字符。找到:0)。
测试:
```java
@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 {
// 依赖注入
@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("无效的Authorization标头 - '" + 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("在身份验证(令牌验证)期间发生异常", e);
throw new AuthException("认证错误");
}
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 错误:" + e.getMessage();
}
}
英文:
I am trying to write a JwtTokenVerifier test using MockMvc
When I trying to request some API with an invalid Auth header: instead of returning a response with 4xx status code, it throws an internal exception (AuthException in my case).
Should I expect this exception in the test
or I need to do something to get a response?
(test succeeds for values "" and "123", but fails for "Bearer qewqweqweqwe" with an AuthException (io.jsonwebtoken.MalformedJwtException: JWT strings must contain exactly 2 period characters. Found: 0))
Test:
@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 filter:
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();
}
}
答案1
得分: 0
看起来一切都连接正常并且工作正常,但是你的JWT解析器识别出你提供的Bearer令牌甚至不是有效的JWT格式,因此它在抛出异常。
我建议按照正确的格式创建一个JWT,但是在你的应用程序上下文中没有意义,以测试你试图验证的行为。可以尝试使用 jwt.io。
英文:
It looks as though everything is wired up and working properly, however your JWT parser recognises that the Bearer token you are providing isn't even a valid format for a JWT, so it's throwing exception.
I'd suggest creating a JWT in the correct format <b>BUT</b> that does not make sense in your application context to test the behaviour you're trying to verify. Try jwt.io.
答案2
得分: 0
我意识到我的 apiExceptionHandler 对于这个过滤器不起作用。
根据 Java Servlet 规范,过滤器在调用 Servlet 之前总是会执行。现在,@ControllerAdvice 只对在 DispatcherServlet 内部执行的控制器有用。因此,使用过滤器并期望 @ControllerAdvice,或在这种情况下的 @ExceptionHandler 会被调用,是不会发生的。
为了获得适当的响应,我需要手动提供异常处理程序。
在这个链接中可以找到相关信息:https://stackoverflow.com/questions/16669356/testing-spring-mvc-exceptionhandler-method-with-spring-mvc-test/64346625#64346625
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(statusController)
.setControllerAdvice(new ExceptionController())
.build();
英文:
I realized that my apiExceptionHandler does not work for the filter
https://stackoverflow.com/questions/30335157/make-simple-servlet-filter-work-with-controlleradvice :
As specified by the java servlet specification Filters execute always before a Servlet is invoked. Now a @ControllerAdvice is only useful for controller which are executed inside the DispatcherServlet. So using a Filter and expecting a @ControllerAdvice or in this case the @ExceptionHandler, to be invoked isn't going to happen.
and in order to get appropriate response i need to manually provide exception handler
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(statusController)
.setControllerAdvice(new ExceptionController())
.build();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论