Spring security JWT 过滤器抛出 500 和 HTML 而不是 401 和 json
Posted
技术标签:
【中文标题】Spring security JWT 过滤器抛出 500 和 HTML 而不是 401 和 json【英文标题】:Spring security JWT filter throws 500 and HTML instead of 401 and json 【发布时间】:2020-11-04 06:33:00 【问题描述】:我一直无法让安全系统正常工作,这个问题已经解决了一半 Spring Boot Security wont ignore certain paths that dont need to be secured 第二个问题是spring忽略了失败的HTTP状态码,总是抛出500。
当 JWT 令牌无效时,我想返回 401 和 json 响应。我不断得到一个 500 和白色标签的 html 页面。 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)
在我的应用程序类中我也有
@SpringBootApplication(exclude = [ErrorMvcAutoConfiguration::class])
应用程序中的其他任何地方 ResponseStatusException 都会以正确的代码和 JSON 格式抛出错误,例如,当我抛出异常时,响应将是 HTML 之类的
HTTP 状态 500 – 内部服务器错误 身体 字体系列:Tahoma、Arial、无衬线字体;
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>
HTTP 状态 500 – 内部服务器错误
类型异常报告
消息 401 UNAUTHORIZED "Expired JWT token"
描述 服务器遇到了一个意外情况,导致它无法完成请求。
异常
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)
注意服务器日志中提供了根本原因的完整堆栈跟踪。
Apache Tomcat/9.0.35
【问题讨论】:
我仍然不明白您为什么要编写自己的过滤器和 oauth 支持。它在 Spring/Spring Boot 中都是开箱即用的。授权服务器,如果你是强迫症,在重写时暂时被弃用(大多数人认为这很愚蠢——重写它,然后弃用旧版本哈哈)。 Imo,仍然没有理由编写自己的与经过实战测试的解决方案。您还可以使用 Keycloak 等。 【参考方案1】:不要在过滤器中抛出异常,而是这样做
response.sendsetStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
或者如果你也想要消息
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;
【讨论】:
这工作正常!但我认为那将是 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);【参考方案2】:这个问题困扰了我三天左右,感谢@Kavithakaran Kanapathippillai 的评论。
我的做法是这样的
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)
【讨论】:
【参考方案3】:如果您正在设置 SecurityWebFilterChain 使用的 ServerAuthenticationEntryPoint
(与 @EnableWebFluxSecurity 一起提供),您可以使用它
class HttpBasicServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint
由于某种原因,当我尝试实现自己的入口点时,我得到了 500 而不是 401。但是,它按预期工作,现在抛出 401。
【讨论】:
以上是关于Spring security JWT 过滤器抛出 500 和 HTML 而不是 401 和 json的主要内容,如果未能解决你的问题,请参考以下文章
JWT spring security Authentication过滤器丢失标头信息
社交登录,spring-security-oauth2 和 spring-security-jwt?