当没有 Bearer 令牌时,Spring Security 返回 403

问题描述 投票:0回答:4

我还没有找到解决问题的正确方法。我正在使用 Spring Security Oauth2 资源服务器来验证我的请求。效果很好。但是当使用不同的场景进行测试时,发现如果不存在 Authorization 标头,或者存在 Authorization 标头但该值不以 Bearer 开头,则 Spring Security 返回 403 而不是 401 。

Spring Boot Starter - 2.6.7
Spring Boot Starter Security - 2.6.7
Spring Security Config & Web - 5.6.3
Spring Security Core - 5.3.19
Spring Boot Starter OAuth2 Resource Server - 2.6.7
Spring OAuth2 Resource Server - 5.6.3

我指的是这个答案并为BearerTokenAuthenticationEntryPoint添加了以下代码。不同之处在于我使用 introspection url 而不是 jwt。 但这没有帮助,并且该部分没有被执行。如果不记名令牌存在,则只有它被执行。

我在这里缺少什么?

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class CustomResourceServerSecurityConfiguration {

    @Value("${spring.security.oauth2.resourceserver.opaque-token.introspection-uri}")
    String introspectionUri;

    @Value("${spring.security.oauth2.resourceserver.opaque-token.client-id}")
    String clientId;

    @Value("${spring.security.oauth2.resourceserver.opaque-token.client-secret}")
    String clientSecret;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
                .oauth2ResourceServer(oauth2 -> oauth2
                        .opaqueToken(opaque -> opaque.introspectionUri(this.introspectionUri)
                                .introspectionClientCredentials(this.clientId, this.clientSecret))
                        .authenticationEntryPoint((request, response, exception) -> {
                            System.out.println("Authentication failed");
                            BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
                            delegate.commence(request, response, exception);
                        }))

                .exceptionHandling(
                        (exceptions) -> exceptions.authenticationEntryPoint((request, response, exception) -> {
                            System.out.println("Authentication is required");
                            BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
                            delegate.commence(request, response, exception);
                        }));
        return http.build();
    }
}
java spring-security spring-security-oauth2 spring-oauth2
4个回答
3
投票

如果您的场景是 POST 收到 403,GET 收到 401(如果缺少 Bearer 令牌),则与 csrf 有关。

44.2.14 我在执行 POST 时收到 403 Forbidden

如果 HTTP POST 返回 HTTP 403 Forbidden,但适用于 HTTP GET,则问题很可能与 CSRF 有关。提供 CSRF 令牌或禁用 CSRF 保护(不推荐)。

来源在这里

如果您使用 JWT 令牌,那么如果没有任何其他要求,那么您可以禁用它。

  @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
          http.authorizeHttpRequests(authorize -> authorize
                        .anyRequest().authenticated())
                        .csrf().disable()           
                 return http.build();
    } 

4。无状态 Spring API

如果我们的无状态API使用基于令牌的身份验证,例如JWT,我们不需要CSRF保护,并且我们必须像之前看到的那样禁用它

来源


1
投票

使用默认配置,当授权标头丢失或无效(格式错误、过期、错误的颁发者等)时,您应该会收到 302(重定向到登录)。如果您有 403,那么您将面临另一个异常(CSRF、CORS 或其他)。设置

logging.level.org.sprngframework.security=DEBUG
并仔细检查日志

要更改此默认行为(401 而不是 302),请像 那些教程中所做的那样:

        http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
            response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
            response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
        });

在示例中,例如用于 servlet 和令牌内省的示例,它满足您的具体用例,您甚至可以找到断言 http 状态符合您的预期的单元测试:未经授权时为 401,被拒绝时为 403:

    @Test
// security-context not set for this test => anonymous
    void greetWitoutAuthentication() throws Exception {
        api.get("/greet").andExpect(status().isUnauthorized());
    }

    @Test
@WithMockBearerTokenAuthentication(authorities = "ROLE_AUTHORIZED_PERSONNEL", attributes = @OpenIdClaims(sub = "Ch4mpy"))
    void securedRouteWithAuthorizedPersonnelIsOk() throws Exception {
        api.get("/secured-route").andExpect(status().isOk());
    }

    @Test
@WithMockBearerTokenAuthentication(authorities = "NOT_A_REQUIRED_ROLE")
    void securedMethodWithoutAuthorizedPersonnelIsForbidden() throws Exception {
        api.get("/secured-method").andExpect(status().isForbidden());
    }

当然,这些测试通过了...


0
投票

我也有同样的问题,我只是不明白为什么当请求标头中没有令牌时,安全团队会在默认的 AuthenticationEntryPoint 中返回 403。

我的解决方案是自定义 AuthenticationEntryPoint:

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    private static final Log logger = LogFactory.getLog(CustomAuthenticationEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        logger.debug("Pre-authenticated entry point called. Rejecting access");
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
    }
}

0
投票

如果您在使用具有正确客户端 ID 和客户端密钥的 Spring OAuth2 客户端时遇到 403 错误,则可以检查和排除故障。

  1. 检查客户凭据。确保您使用的客户端 ID 和密钥正确且与 Oauth2 提供商相对应。 您正在集成时请仔细检查任何拼写错误或多余空格

2)使用客户端 ID 和密码以及授权 url 检查邮递员。

3)授权授予类型。

4)范围权限

5)令牌端点访问。

6)错误信息

7) 代币到期

© www.soinside.com 2019 - 2024. All rights reserved.