Spring Security的BasicAuthenticationFilter返回403而不是401。

3

我已经实现了JWT身份认证和授权。除了未授权的情况之外,一切都运行良好。

未授权的情况: 在没有提供授权令牌的情况下对路由进行HTTP调用。

结果: 返回403禁止访问而不是未经授权。

这是我的代码:

@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
    String header = req.getHeader(HEADER_STRING);

    if (header == null || !header.startsWith(TOKEN_PREFIX)) {
        chain.doFilter(req, res);
        return;
    }

    UsernamePasswordAuthenticationToken authentication = getAuthentication(req);

    SecurityContextHolder.getContext().setAuthentication(authentication);
    chain.doFilter(req, res);
}

在完成

    if (header == null || !header.startsWith(TOKEN_PREFIX)) {
        chain.doFilter(req, res);
        return;
    } 

执行后,响应为403。

以下是我的完整类:

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
        String header = req.getHeader(HEADER_STRING);

        if (header == null || !header.startsWith(TOKEN_PREFIX)) {
            chain.doFilter(req, res);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthentication(req);

        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(req, res);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {

        String token = request.getHeader(HEADER_STRING);
        if (token != null) {

            // setting the user in the security context
            String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
                    .build()
                    .verify(token.replace(TOKEN_PREFIX, ""))
                    .getSubject();

            if(user != null){
                return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
            }

            return null;
        }

        return null;
    }
}

备注:

我遇到了与UsernamePasswordAuthenticationFilter相关的相同问题,并通过覆盖默认的authenticationFailureHandler来解决它:

setAuthenticationFailureHandler(new JWTAuthenticationFailureHandler());

我该如何获得正确的401响应码和响应正文?

谢谢!


第二种解决方案(res.sendError())应该可以工作,但我会尝试寻找另一种解决方案,因为我需要在许多地方这样做,而且这太麻烦了。抛出BadCredentialsException也行不通。 - Andrew T
明天我会看一下 authenticationEntryPoint。我认为如果我将其传递给构造函数,那么 commence 方法应该被调用,你能给我一个提示吗?提供 BasicAuthenticationEntryPoint 的新实例是否足够?谢谢! - Andrew T
2个回答

2
如果您查看正在使用JWTAuthorizationFilter覆盖的BasicAuthenticationFilter在身份验证失败时执行的操作,它会调用authenticationEntryPoint.commence(request, response, failed),该操作发送401。 BasicAuthenticationEntryPoint
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException {
        response.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
        response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
    }

但是你的行为被覆盖并且返回了 null。因此,请尝试以下方法之一:

  • 在返回 null 的位置抛出 BadCredentialsException
  • 执行 response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());

-1
也许这会帮助到遇到类似问题的人在使用Spring Boot 3.1.x时遇到了类似的问题,我在我的securityFilterChain方法中解决了它。解决方案是在filterChain中添加以下行。
.httpBasic(Customizer.withDefaults());

没有配置HTTP基本身份验证,服务器不会在未经授权的请求中发送401未经授权状态,并附带WWW-Authenticate头;相反,它可能会简单地拒绝访问或根据您的安全设置重定向到登录页面。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接