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 日志后:
在您的邮递员中,我没有看到 X-XSRF-TOKEN 标头。如果您\xe2\x80\x99在从cookie中获取XSRF令牌后没有将其发送回服务器,您可能想要按照答案末尾所示的方式执行此操作,因为这是它旨在保护的方式之一对抗CSRF 攻击并且只能这样工作。在像 Angular 这样的框架中,我们可以从 Spring Boot 服务器获取它作为 Cookie 并将其作为标头发送回来,以区分访问相同 URL 的恶意站点,因为此类站点在浏览器内部无法访问与我们的真实域关联的 Cookie将其作为标头发送回来。
\n这是一个简单的工作项目,它使用 spring security 6 和 crsf 令牌以及邮递员测试(如果有帮助的话)。它使用 InMemoryUserDetailsManager、NoOpPasswordEncoder(不建议用于生产)和基本身份验证。
\n安全配置:
\nimport 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\nRun Code Online (Sandbox Code Playgroud)\n控制器:
\nimport 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\nRun Code Online (Sandbox Code Playgroud)\n数据对象模型:
\nimport 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\nRun Code Online (Sandbox Code Playgroud)\n应用程序属性:
\nlogging.level.org.springframework.security.web.csrf=TRACE\nRun Code Online (Sandbox Code Playgroud)\npom.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>\nRun Code Online (Sandbox Code Playgroud)\n在邮递员中使用 CSRF 令牌进行测试:
\n首先添加基本身份验证凭据 -
\n\n添加数据对象json主体-
\n\n向服务器发送模拟请求以获取 XSRF Cookie
\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}}”(双花括号中的环境变量名称,不带引号)。然后在“测试”选项卡中添加以下脚本 -
\nimport 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\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
5619 次 |
| 最近记录: |