Reputation: 4408
I've been having trouble getting security working correctly, half of the problem was fixed with this Spring Boot Security wont ignore certain paths that dont need to be secured Second problem is spring is ignoring the HTTP status code on failure and always throws a 500.
When the JWT token is invalid I want to return a 401 and a json response. I keep getting a 500 and the white label html page. JwtFilter
class JwtFilter(private val tokenService: TokenService) : GenericFilterBean() {
override fun doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain) {
val request = req as HttpServletRequest
val response = res as HttpServletResponse
val httpRequest = request as HttpServletRequest
val path = httpRequest.servletPath.toString().substring(0, 12)
if (path == "/api/v1/auth") {
chain.doFilter(req, res)
return
} else {
val token = TokenUtil.extractToken(request as HttpServletRequest)
if (token != null && token.isNotEmpty()) {
try {
tokenService.getClaims(token)
} catch (e: SignatureException) {
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid JWT Signature")
} catch (e: MalformedJwtException) {
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid JWT token")
} catch (e: ExpiredJwtException) {
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Expired JWT token")
} catch (e: UnsupportedJwtException) {
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Unsupported JWT exception")
} catch (e: IllegalArgumentException) {
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Jwt claims string is empty")
}
} else {
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Missing auth token")
}
chain.doFilter(req, res)
}
}
}
In my application class too I also have
@SpringBootApplication(exclude = [ErrorMvcAutoConfiguration::class])
Everywhere else in the application ResponseStatusException throws an error with the correct code and in JSON format, here for example when I throw an exception the response will be HTML like
<!doctype html>
HTTP Status 500 – Internal Server Error body { font-family: Tahoma, Arial, sans-serif; } h1,
h2,
h3,
b {
color: white;
background-color: #525D76;
}
h1 {
font-size: 22px;
}
h2 {
font-size: 16px;
}
h3 {
font-size: 14px;
}
p {
font-size: 12px;
}
a {
color: black;
}
.line {
height: 1px;
background-color: #525D76;
border: none;
}
</style>
Type Exception Report
Message 401 UNAUTHORIZED "Expired JWT token"
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
org.springframework.web.server.ResponseStatusException: 401 UNAUTHORIZED "Expired JWT token" events.slap.app.web.security.JwtFilter.doFilter(JwtFilter.kt:40) org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
Note The full stack trace of the root cause is available in the server logs.
Upvotes: 13
Views: 9665
Reputation: 71
If you are setting up an ServerAuthenticationEntryPoint
that is used by SecurityWebFilterChain (comes with @EnableWebFluxSecurity) you can use this
class HttpBasicServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint
For some reason, I got 500 thrown instead of 401 when I tried to implement my own EntryPoint. It however works as expected and throws 401 now.
Upvotes: 0
Reputation: 2069
This problem has been bothered me around three-day and thanks @Kavithakaran Kanapathippillai 's comment.
Following is the way I did
if (token != null && token.isNotEmpty()) {
String msg = new String();
try {
tokenService.getClaims(token)
} catch (SignatureException ex) {
msg = "Invalid JWT signature";
} catch (MalformedJwtException ex) {
msg = "Invalid JWT token";
} catch (ExpiredJwtException ex) {
msg = "Expired JWT token";
} catch (UnsupportedJwtException ex) {
msg = "Unsupported JWT token";
} catch (IllegalArgumentException ex) {
msg = "JWT claims string is empty.";
}
if (msg.isNotEmpty()) {
StringBuilder sb = new StringBuilder();
sb.append("{ ");
sb.append("\"error\": \"Unauthorized\",");
sb.append("\"message\": \"Invalid Token.\",");
sb.append("\"path\": \"")
.append(request.getRequestURL())
.append("\"");
sb.append("} ");
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(sb.toString());
return;
}
chain.doFilter(req, res)
Upvotes: 1
Reputation: 8205
Instead of throwing exceptions in the filter, do this
response.sendsetStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
or if you want message as well
StringBuilder sb = new StringBuilder();
sb.append("{ ");
sb.append("\"error\": \"Unauthorized\" ");
sb.append("\"message\": \"Unauthorized\"");<--- your message here
sb.append("\"path\": \"")
.append(request.getRequestURL())
.append("\"");
sb.append("} ");
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(sb.toString());
return;
Upvotes: 11