spring - Spring Boot 安全不能禁用 CSRF 保护
问题描述
我的 Spring Boot REST API 受 Keycloak 实例的保护。由于 CSRF 保护只允许 GET 和 POST,我想禁用它。但是,我的方法似乎不起作用,因为 REST API 将为任何来源与http://localhost:8080不同的请求返回 HTTP 状态 403 。这是我配置安全性的方式:
package de.longnguyen.security;
import de.longnguyen.controller.KeycloakController;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.session.SessionManagementFilter;
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
private static final String[] ALLOWED = new String[]{"/", "/static/**", "/v2/api-docs", "/swagger*/**", "/webjars/**"};
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring()
.antMatchers(ALLOWED);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.cors()
.and()
.authorizeRequests()
.antMatchers(ALLOWED).permitAll()
.anyRequest()
.authenticated()
.and()
.addFilterBefore(new CustomCorsFilter(), SessionManagementFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable();
}
}
Origin 为http://localhost:8080的请求将起作用:
但是,来源为http://localhost:3000的完全相同的请求将不起作用:
编辑:
这就是我的 CustomCorsFilter 的样子:
package de.longnguyen.security;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@Slf4j
public class CustomCorsFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Vary", "Origin");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-CSRF-TOKEN");
}
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {
}
}
编辑编辑:
这就是我的日志的样子:
2020-03-26 23:08:45.758 DEBUG 27582 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/v1/keycloak/delete'; against '/'
2020-03-26 23:08:45.758 DEBUG 27582 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/v1/keycloak/delete'; against '/static/**'
2020-03-26 23:08:45.758 DEBUG 27582 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/v1/keycloak/delete'; against '/v2/api-docs'
2020-03-26 23:08:45.758 DEBUG 27582 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/v1/keycloak/delete'; against '/swagger*/**'
2020-03-26 23:08:45.758 DEBUG 27582 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/v1/keycloak/delete'; against '/webjars/**'
2020-03-26 23:08:45.758 DEBUG 27582 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/keycloak/delete at position 1 of 17 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-03-26 23:08:45.758 DEBUG 27582 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/keycloak/delete at position 2 of 17 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-03-26 23:08:45.758 DEBUG 27582 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/keycloak/delete at position 3 of 17 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-03-26 23:08:45.758 DEBUG 27582 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/keycloak/delete at position 4 of 17 in additional filter chain; firing Filter: 'CorsFilter'
2020-03-26 23:08:45.759 DEBUG 27582 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.http.ResponseEntity<java.lang.String> de.longnguyen.controller.KeycloakController.delete()
2020-03-26 23:08:45.760 DEBUG 27582 --- [nio-8080-exec-2] o.s.web.cors.DefaultCorsProcessor : Reject: HTTP 'DELETE' is not allowed
2020-03-26 23:08:45.761 DEBUG 27582 --- [nio-8080-exec-2] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@3fffd0ea
2020-03-26 23:08:45.761 DEBUG 27582 --- [nio-8080-exec-2] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
解决方案
您可以尝试将方法更改为以下。
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
...
}
而不是添加.addFilterBefore(new CustomCorsFilter(),
,您可以在您的配置中创建一个 Bean,例如
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "DELETE", "PUT", "PATCH"));
configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
SessionManagementFilter 也可能相同。
推荐阅读
- javascript - vue-draggable - 不能直接改变我的数组
- uwp - 如何从 ItemTemplate UWP 中的 DataContext 获取命令
- java - SQLite 数据库 - 更新 RecyclerView 中的一行
- google-apps-script - 连接到外部 API Google Apps 脚本时出现错误 401
- python - 正确处理土耳其语大写和小写,需要修改/覆盖内置函数?
- unix - 如何使用unix替换文件中某个字符串之后的文本?
- apache-httpclient-4.x - 使用 Apache Async http 客户端时只需要响应流
- c# - Unity - 相机 ScreenToWorldPoint 返回奇数值
- reactjs - 在 React 组件中使用 NPM 包
- react-native - 有没有办法在原生组件上强制执行道具的顺序?