如何从 JWT 身份验证过滤器中抛出自定义异常
Posted
技术标签:
【中文标题】如何从 JWT 身份验证过滤器中抛出自定义异常【英文标题】:How to throw Custom Exception from JWT Authentication Filter 【发布时间】:2021-03-12 04:15:45 【问题描述】:我在 Project 中使用带有 JWT 和 Spring Security 的 Spring Boot。 春季启动:2.3.5.RELEASE JJWT 版本:0.11.2
我已经创建了用于中断请求的 JWT 身份验证过滤器类。如果请求在标头中包含 JWT 令牌,则解析令牌、获取角色并在 spring 安全上下文中设置身份验证对象。我的身份验证过滤器类如下
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import io.jsonwebtoken.Claims;
public class JwtAuthenticationFilter extends OncePerRequestFilter
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException
String header = request.getHeader(JwtConstant.AUTHORIZATION);
if (StringUtils.isNotBlank(header) && header.startsWith(JwtConstant.BEARER))
String authToken = header.replace(JwtConstant.BEARER, "");
Claims claims = jwtTokenUtil.getJwtClaims(authToken);
String username = claims.getSubject();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,
"", getAuthoritiesFromString(claims));
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
logger.info("authenticated user " + username + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
private Collection<? extends GrantedAuthority> getAuthoritiesFromString(Claims claims)
return Arrays.stream(claims.get(JwtConstant.AUTHORITIES).toString().split(",")).map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
令牌解析方法如下:
public Claims getJwtClaims(String token)
Claims claims = null;
try
claims = Jwts.parserBuilder().setSigningKey(getPublicKey()).build().parseClaimsJws(token).getBody();
catch (ExpiredJwtException e)
throw new CustomException(env.getProperty(ExceptionMessage.TOKEN_EXPIRED),e, ErrorCode.TOKEN_EXPIRE);
catch (SignatureException | MalformedJwtException e)
throw new CustomException(env.getProperty(ExceptionMessage.TOKEN_INVALID),e, ErrorCode.TOKEN_INVALID);
catch (Exception e)
throw new CustomException(env.getProperty(ExceptionMessage.TOKEN_PARSING),e, ErrorCode.INTERNAL_SERVER_ERROR);
return claims;
如果 oken 无效或过期,则在解析令牌时,我将抛出具有 Integer errorCode 的自定义异常。
当请求包含过期或无效令牌时,首先它会进入 jwt 身份验证过滤器类。在解析令牌时,我得到以下响应:
"timestamp": "2020-11-30T08:28:40.319+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "Token Expired",
"path": "/api/users/profile"
因为这个异常是从 servlet 过滤器抛出的,它不会去 @RestControllerAdvise 和它抛出的内置异常响应。在这种情况下,我想用错误代码抛出我的自定义异常类。我尝试了不同的解决方案,但无法解决我的问题。有没有办法从这种情况下抛出自定义错误代码。
我想要下面的异常响应,这是我的自定义响应类:
"message": "Token Expired",
"code": 101,
"error": true,
"timestamp": "2020-11-30T08:31:46.911+00:00"
【问题讨论】:
【参考方案1】:为 "/error" 创建一个 customerErrorController,例如:
@RestController
public class CustomErrorController implements ErrorController
private static final String PATH = "/error";
@RequestMapping(PATH)
public ResponseEntity<ErrorDetails> handleError(final HttpServletRequest request,
final HttpServletResponse response) throws Throwable
throw (Throwable) request.getAttribute("javax.servlet.error.exception");
@Override
public String getErrorPath()
return PATH;
然后@RestControllerAdvise 将能够选择异常。
【讨论】:
它的工作。但是在这种情况下如何处理身份验证和 InsufficientAuthenticationException 异常?我正在为此使用身份验证入口点。 过滤器中抛出的异常将转到“/error”端点。如果在@RestControllerAdvise 中处理了该异常,并且像上面的示例一样覆盖了“/error” [可能需要从 HttpServletRequest 获取一些其他属性],那么将自动处理自定义异常。以上是关于如何从 JWT 身份验证过滤器中抛出自定义异常的主要内容,如果未能解决你的问题,请参考以下文章
无法为 JWT CustomClaimVerifier 抛出自定义异常消息