spring-security 当没有承载令牌时,Spring Security返回403

ozxc1zmp  于 2022-11-11  发布在  Spring
关注(0)|答案(3)|浏览(257)

我还没有找到一个合适的解决方案来解决我的问题。我使用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

我指的是this answer,并在下面添加了 BearerTokenAuthenticationEntryPoint 的代码。不同之处在于我使用的是内省url而不是jwt。但这没有帮助,该部分不会被执行。如果存在Bearer令牌,则只有它会被执行。
我错过了什么?

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();
    }
}
42fyovps

42fyovps1#

如果您的场景是,在缺少Bearer令牌的情况下,POST得到403,GET得到401,这与csrf有关。

44.2.14我在执行POST时得到403禁止

如果针对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保护,并且必须像前面看到的那样禁用它
Source

wb1gzix0

wb1gzix02#

使用默认配置时,当授权头丢失或无效(格式错误、过期、错误的颁发者等)时,您应该有一个302(重定向到登录)。如果您有一个403,那么您将面临另一个异常(CSRF、CORS或其他)。设置logging.level.org.sprngframework.security=DEBUG并仔细检查日志
要更改此默认行为(401而不是302),请按照those tutorials中的方式进行操作:

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

the one for servlets and token introspection这样的示例中,它完全符合您的用例,您甚至可以找到Asserthttp状态是您所期望的状态的单元测试:未授权时为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());
    }

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

j1dl9f46

j1dl9f463#

我做的更改是,删除了 @EnableWebSecurity 注解,并使用 WebSecurityConfigurerAdapter 扩展了类,并使用相同的内容覆盖了 configure(HttpSecurity)
我的最后一堂课如下。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class CustomResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @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;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);

        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);
                        }))
                ;

    }
}

相关问题