Spring Security 6 POST请求未经permitAll()授权

Oct*_*via 5 spring spring-security spring-boot

我正在使用 Spring Boot 3、Spring Security 6。我的安全配置无法正常工作。我有两条路径,任何请求都应该被允许,而对于其他所有请求,都需要进行身份验证。

GET和方法都POST适用于那些需要身份验证的人。

对于那些带有 的permitAll(),只有GET请求有效。对于POST,我收到 401 Unauthorized。

我负责 CSRF,无论如何我希望所有POST请求都能工作,而不仅仅是那些带有身份验证的请求。

在 Postman 上,我选择POST“无身份验证”,放置 RAW 正文并选择 JSON。我真的不知道为什么它不起作用。

这是我的代码:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

 
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, KeycloakLogoutHandler keycloakLogoutHandler) throws Exception {

        CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
        XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
        // set the name of the attribute the CsrfToken will be populated on
        delegate.setCsrfRequestAttributeName("_csrf");
        // Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
        // default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
        CsrfTokenRequestHandler requestHandler = delegate::handle;
    
        http
                .authorizeHttpRequests().requestMatchers("/firstpath/**", "/secondpath/**", "/error/**").permitAll().and()
                .authorizeHttpRequests().anyRequest().authenticated().and()
                .oauth2ResourceServer(oauth2 -> oauth2.jwt());
        http.oauth2Login()
                .and()
                .logout()
                .addLogoutHandler(keycloakLogoutHandler)
                .logoutSuccessUrl("/");
        http.csrf((csrf) -> csrf
                .csrfTokenRepository(tokenRepository)
                .csrfTokenRequestHandler(requestHandler));
        return http.build();
    }
}
Run Code Online (Sandbox Code Playgroud)
@Slf4j
@RestController
@RequestMapping("/firstpath")
public class NameitController {

    @PostMapping(value = "path", produces = WSConstants.JSON_MEDIATYPE)
    @ResponseBody
    public ResponseEntity saveMyObject(@RequestBody ObjectDTO dto) {
        [...] //my code
    }
}
Run Code Online (Sandbox Code Playgroud)

我也尝试过 http.authorizeHttpRequests().requestMatchers(HttpMethod.POST, "/firstpath/path").permitAll(),但是没有用。

编辑:它仍然与 CSRF 保护有关,因为当我累了时 http.csrf().disable();,一切都工作正常。但我仍然想要 CSRF 保护,似乎令牌没有发送permitAll()?...

通过邮递员发布请求

Edit2:添加 Spring Security 日志后:

在此输入图像描述

Sid*_*pai 5

在您的邮递员中,我没有看到 X-XSRF-TOKEN 标头。如果您\xe2\x80\x99在从cookie中获取XSRF令牌后没有将其发送回服务器,您可能想要按照答案末尾所示的方式执行此操作,因为这是它旨在保护的方式之一对抗CSRF 攻击并且只能这样工作。在像 Angular 这样的框架中,我们可以从 Spring Boot 服务器获取它作为 Cookie 并将其作为标头发送回来,以区分访问相同 URL 的恶意站点,因为此类站点在浏览器内部无法访问与我们的真实域关联的 Cookie将其作为标头发送回来。

\n

这是一个简单的工作项目,它使用 spring security 6 和 crsf 令牌以及邮递员测试(如果有帮助的话)。它使用 InMemoryUserDetailsManager、NoOpPasswordEncoder(不建议用于生产)和基本身份验证。

\n

安全配置:

\n
import java.util.function.Supplier;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.csrf.CookieCsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler;\nimport org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n@Configuration\npublic class ProjectSecurityConfig {\n    @Bean\n    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {\n        XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();\n        delegate.setCsrfRequestAttributeName("_csrf");\n        CsrfTokenRequestHandler requestHandler = new CsrfTokenRequestHandler() {\n            @Override\n            public void handle(HttpServletRequest request, HttpServletResponse response,\n                    Supplier<CsrfToken> csrfToken) {\n                delegate.handle(request, response, csrfToken);\n            }\n        };\n        return http\n                .cors().disable() // disabled cors for simplicity in this example in case of testing through a ui\n                .authorizeHttpRequests()\n                .requestMatchers("/error").permitAll()\n                .anyRequest().authenticated()\n                .and()\n                .csrf()\n                .csrfTokenRequestHandler(requestHandler)\n                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())\n                .and().formLogin()\n                .and().httpBasic()\n                .and().build();\n    }\n\n    @Bean\n    InMemoryUserDetailsManager userDetailsService() {\n        UserDetails admin = User.withUsername("admin").password("pass").authorities("admin").build();\n        return new InMemoryUserDetailsManager(admin);\n    }\n\n    @Bean\n    PasswordEncoder passwordEncoder() {\n        return NoOpPasswordEncoder.getInstance();\n    }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

控制器:

\n
import org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.learning.entity.DataObject;\n\n@RestController\npublic class TestController {\n    @PostMapping("/post")\n    public String post(@RequestBody DataObject dataObject) {\n        return "succesfull post";\n    }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

数据对象模型:

\n
import java.util.function.Supplier;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.csrf.CookieCsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler;\nimport org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n@Configuration\npublic class ProjectSecurityConfig {\n    @Bean\n    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {\n        XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();\n        delegate.setCsrfRequestAttributeName("_csrf");\n        CsrfTokenRequestHandler requestHandler = new CsrfTokenRequestHandler() {\n            @Override\n            public void handle(HttpServletRequest request, HttpServletResponse response,\n                    Supplier<CsrfToken> csrfToken) {\n                delegate.handle(request, response, csrfToken);\n            }\n        };\n        return http\n                .cors().disable() // disabled cors for simplicity in this example in case of testing through a ui\n                .authorizeHttpRequests()\n                .requestMatchers("/error").permitAll()\n                .anyRequest().authenticated()\n                .and()\n                .csrf()\n                .csrfTokenRequestHandler(requestHandler)\n                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())\n                .and().formLogin()\n                .and().httpBasic()\n                .and().build();\n    }\n\n    @Bean\n    InMemoryUserDetailsManager userDetailsService() {\n        UserDetails admin = User.withUsername("admin").password("pass").authorities("admin").build();\n        return new InMemoryUserDetailsManager(admin);\n    }\n\n    @Bean\n    PasswordEncoder passwordEncoder() {\n        return NoOpPasswordEncoder.getInstance();\n    }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

应用程序属性:

\n
logging.level.org.springframework.security.web.csrf=TRACE\n
Run Code Online (Sandbox Code Playgroud)\n

pom.xml:

\n
<?xml version="1.0" encoding="UTF-8"?>\n<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>3.0.1</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.learning</groupId>\n    <artifactId>spring-security-3-csrf-example</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-security-3-csrf-example</name>\n    <description>spring learning</description>\n    <properties>\n        <java.version>17</java.version>\n    </properties>\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-devtools</artifactId>\n            <scope>runtime</scope>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.security</groupId>\n            <artifactId>spring-security-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n
Run Code Online (Sandbox Code Playgroud)\n

在邮递员中使用 CSRF 令牌进行测试:

\n

首先添加基本身份验证凭据 -

\n

添加基本​​身份验证凭据

\n

添加数据对象json主体-

\n

添加对象 json 主体

\n

向服务器发送模拟请求以获取 XSRF Coo​​kie

\n

使用此 Cookie 值作为名称为“X-XSRF-TOKEN”的标头 -

\n

在此输入图像描述

\n

测试一下——

\n

在此输入图像描述

\n

注意:- 从版本 6 开始,Spring Security 默认情况下不会创建用于基本身份验证的会话,因此本示例中不会返回会话的 Cookie。

\n

更新 :-

\n

这是一篇关于通过邮递员发送 XSRF-TOKEN 的更复杂方法的文章,正如@OctaviaAdler 在评论中指出的那样。TLDR,以防链接断开:- 在 postman 中创建一个环境并在其中添加变量“xsrf-token”。在请求中,添加标头 X-XSRF-TOKEN,并将值设置为“{{xsrf-token}}”(双花括号中的环境变量名称,不带引号)。然后在“测试”选项卡中添加以下脚本 -

\n
import org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.learning.entity.DataObject;\n\n@RestController\npublic class TestController {\n    @PostMapping("/post")\n    public String post(@RequestBody DataObject dataObject) {\n        return "succesfull post";\n    }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n