尽管有 SecurityConfig,但 Spring Security 会阻止 POST 请求
Posted
技术标签:
【中文标题】尽管有 SecurityConfig,但 Spring Security 会阻止 POST 请求【英文标题】:Spring Security blocks POST requests despite SecurityConfig 【发布时间】:2018-12-04 05:15:47 【问题描述】:我正在开发一个基于 Spring Boot (spring-boot-starter-web
) 的 REST API,我使用 Spring Security (spring-security-core
e spring-security-config
) 来保护不同的端点。
身份验证是通过使用本地数据库完成的,该数据库包含具有两组不同角色的用户:ADMIN
和USER
。 USER
应该能够GET
所有API 端点和POST
到基于routeA
的端点。 ADMIN
应该能够与USER
加上POST
和DELETE
对基于`routeB
但是我得到的行为是我可以向任何端点发出 GET
请求,但 POST
请求总是为任何类型的用户返回 HTTP 403 Forbidden
- ADMIN
和 USER
- 这不是预期的根据我的SecurityConfiguration
,我期待什么。
关于我缺少什么的任何想法?
SecurityConfiguration.java
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
@Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private DataSource dataSource;
@Override
public void configure(AuthenticationManagerBuilder builder) throws Exception
logger.info("Using database as the authentication provider.");
builder.jdbcAuthentication().dataSource(dataSource).passwordEncoder(new BCryptPasswordEncoder());
@Override
protected void configure(HttpSecurity http) throws Exception
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeA/*").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeB/*").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/routeB/*").hasRole("ADMIN").and().
requestCache().requestCache(new NullRequestCache()).and().
httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
cors();
@Bean
public CorsConfigurationSource corsConfigurationSource()
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
RouteBController .java
@RestController
public class RouteBController
static final Logger logger = LoggerFactory.getLogger(RouteBController.class);
public RouteBController()
@RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET)
public String getStuff()
return "Got a hello world!";
@RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST)
public String postStuff()
return "Posted a hello world!";
RESTAuthenticationEntryPoint.java
@Component
public class RESTAuthenticationEntryPoint extends BasicAuthenticationEntryPoint
@Override
public void afterPropertiesSet() throws Exception
setRealmName("AppNameHere");
super.afterPropertiesSet();
【问题讨论】:
【参考方案1】:在禁用 CSFR 以解决此问题之前,请查看 Mohd Waseem's answer 上的资源,以更好地了解它的重要性并了解它的重要性正确设置。作为RCaetano has said,CSFR 可以帮助我们免受攻击,不应盲目禁用它。
由于这个答案仍然解释了我原来的问题的 2 个问题,我将把它作为标记的答案,以提高人们对 CSFT 和安全路线可能存在的问题的认识,但 不要从字面上理解.
SecurityConfiguration.java
中有 2 个问题导致其行为不端。
虽然403 Forbidden
错误消息没有包含任何关于失败原因的消息指示(参见下面的示例),但事实证明这是由于启用了CSRF。禁用它允许处理 POST
和 DELETE
请求。
"timestamp": "2018-06-26T09:17:19.672+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/routeB"
另外,antMatched(HttpMethod, String)
中用于RouteB
的表达式也不正确,因为/routeB/*
期望它在/
之后有一些东西。正确的配置是/routeB/**
,因为可以存在更多路径(或不存在)。
更正SecurityConfiguration.java
是
@Override
protected void configure(HttpSecurity http) throws Exception
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeA/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeB/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/routeB/**").hasRole("ADMIN").and().
requestCache().requestCache(new NullRequestCache()).and().
httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
cors().and().
csrf().disable();
来源: *** em Português
【讨论】:
CSRF...呸!谢谢! 我知道这个问题已经存在几个月/几年了,但据我所知,CSRF 不应该在生产中被禁用,不是吗? @Igor 正如在这些 cmets 中所说,不应盲目禁用 CSRF。您可以根据自己的需要对其进行调整,以避免我遇到的问题,同时保持它提供的安全优势 @RCaetano 感谢您的评论,我没有看到之前有人评论过。我会留下答案,因为我相信它会引起人们对 CSFT 和安全路由可能存在的问题的认识,但我指出了这篇文章中分享的更好的解决方案/资源。 大量帮助找到这个问题!除此之外,如果您想选择性地禁用 CSRF(例如在公开公开的 API 上),您可以将 ANT 匹配器添加到 csrf。例如.csrf().ignoringAntMatchers("/some/rest/api")
【参考方案2】:
跨站请求伪造是一种网络安全漏洞,允许攻击者诱使用户执行他们没有执行的操作 打算执行。
在您的情况下,禁用 CSRF 保护会使用户面临此漏洞。
注意:如果它是带有 O-Auth 保护的纯 Rest API,那么 CSRF 不是 需要。 Should I use CSRF protection on Rest API endpoints?
但是在您的情况下,当用户登录会话并返回 cookie 作为响应并且没有 CSRF 令牌时,攻击者可以利用它并执行 CSRF。
禁用 CSRF 并不是一个好主意,您可以将应用配置为在响应标头中返回 CSRF 令牌,然后在所有后续状态更改调用中使用它。
在您的 SecurityConfiguration.java
中添加这行代码// CSRF tokens handling
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
CsrfTokenResponseHeaderBindingFilter.java
public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";
protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";
protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException
CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
if (token != null)
response.setHeader(RESPONSE_HEADER_NAME, token.getHeaderName());
response.setHeader(RESPONSE_PARAM_NAME, token.getParameterName());
response.setHeader(RESPONSE_TOKEN_NAME, token.getToken());
filterChain.doFilter(request, response);
标头响应表单服务器:
请注意,我们现在在标头中有 CSRF 令牌。在会话到期之前,这不会改变。 另请阅读:Spring Security’s CSRF protection for REST services: the client side and the server side 以便更好地理解。
【讨论】:
(Others) 一定要使用正确的导入:org.springframework.security.web.csrf.CsrfToken
或者你可以得到:class org.springframework.security.web.csrf.DefaultCsrfToken cannot be cast to class org.springframework.security.web.server.csrf.CsrfToken
【参考方案3】:
这是一个简单的 CSRF 启用问题,不允许 POST 请求。我遇到了同样的问题,这是解决方案:(解释)
@Override
protected void configure(HttpSecurity http) throws Exception
http
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/form").hasRole("ADMIN") // Specific api method request based on role.
.antMatchers("/home","/basic").permitAll() // permited urls to guest users(without login).
.anyRequest().authenticated()
.and()
.formLogin() // not specified form page to use default login page of spring security
.permitAll()
.and()
.logout().deleteCookies("JSESSIONID") // delete memory of browser after logout
.and()
.rememberMe().key("uniqueAndSecret"); // remember me check box enabled.
http.csrf().disable(); // ADD THIS CODE TO DISABLE CSRF IN PROJECT.**
以上代码:
http.csrf().disable();
会解决问题。
【讨论】:
以上是关于尽管有 SecurityConfig,但 Spring Security 会阻止 POST 请求的主要内容,如果未能解决你的问题,请参考以下文章
无法在 SpringBootApplication 中关闭 SecurityConfig
页面重定向取决于使用 Spring 安全性和 Thymeleaf 的角色 - Spring
UnsatisfiedDependencyException:创建名为“securityConfig”的 bean 时出错