我有一个JWTAuthFilter,它在验证令牌的地方扩展了OncePerRequestFilter
。
validateToken方法将引发自定义异常(扩展org.springframework.security.core.AuthenticationException
的CredentialsChangedException和TooManyDevicesException)
这些异常已正确捕获在过滤器中,但是当它们前进到AuthenticationEntryPoint
时,AuthenticationException变成instanceof
InsufficientAuthenticationException
和我想返回的自定义错误消息响应丢失了。
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
private static final String BEARER = "Bearer ";
private static final Logger logger = LoggerFactory.getLogger(JwtAuthFilter.class);
@Autowired
private UserDetailsService jwtUserDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value ("${jwt.http.request.header}")
private String tokenHeader;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader(this.tokenHeader);
String username = null;
String jwtToken = null;
if((requestTokenHeader != null) && requestTokenHeader.startsWith(BEARER)) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
if((username != null) && (SecurityContextHolder.getContext()
.getAuthentication() == null)) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if(jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext()
.setAuthentication(usernamePasswordAuthenticationToken);
}
}
}
catch (IllegalArgumentException e) {
logger.error("Unable to get username from JWT. ", e.getLocalizedMessage());
}
catch (ExpiredJwtException e) {
logger.warn("Expired JWT. ", e.getLocalizedMessage());
}
catch (Exception e) {
logger.error(e.getLocalizedMessage());
}
}
chain.doFilter(request, response);
}
}
@Component
public class JwtUnAuthorizedResponseAuthenticationEntryPoint implements AuthenticationEntryPoint , Serializable {
private static final long serialVersionUID = - 8970718410437077606L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
throws IOException {
if(e instanceof TooManyDevicesException) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.TOO_MANY_DEVICES);
}
else if(e instanceof CredentialsChangedException) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.CREDENTIALS_CHANGED);
}
else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.JWT_EXPIRED);
}
}
}
我想从我的过滤器发送适当的未经授权的回复,有没有办法做到这一点?
答案 0 :(得分:0)
我认为,一旦您在AuthenticationException
中抓到JwtAuthFilter
,就不应继续使用下一个过滤器,因为很可能AnonymousAuthenticationFilter
将位于过滤器链的后面,如果SecurityContextHolder
为空(即,验证失败时发生),此过滤器会将当前请求配置为匿名用户。 InsufficientAuthenticationException
很可能是由于Spring认为当前请求是访问某些受保护的URL或方法的匿名用户。
相反,一旦您在AuthenticationException
中抓到JwtAuthFilter
,就应该调用AuthenticationEntryPoint.commence()
并结束过滤链。 https://repl.it/@Evandronao/WellmadeMetallicFlashdrives也是现在这样。
所以,我建议将JwtAuthFilter
修改为:
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
try{
//validate JWT
} catch (AuthenticationException e) {
logger.error(e.getLocalizedMessage());
SecurityContextHolder.clearContext();
this.authenticationEntryPoint.commence(request, response, e);
return;
}
}
}
答案 1 :(得分:0)
目前,作为解决方法,我只是将JwtAuthFilter
中的自定义错误添加为request
属性,并在AuthenticationEntryPoint
中检索了它