Spring Security 会阻止 POST 请求,尽管有 SecurityConfig

问题描述 投票:0回答:4

我正在开发一个基于 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
的期待。

我想念什么有什么想法吗?


安全配置.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();
    }
}
spring-boot spring-security
4个回答
47
投票

在禁用 CSFR 作为解决此问题的方法之前,请检查Mohd Waseem 的答案上的资源,以更好地理解它为何如此重要,并了解如何正确设置它。正如 RCaetano 所说CSFR 是为了帮助我们免受攻击,不应该盲目禁用它

由于这个答案仍然解释了我原来问题的两个问题,所以我将其保留为标记答案,以提高人们对 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/*
期望在
/之后有something
。正确的配置是 
/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(); }


来源: StackOverflow em Português


5
投票

跨站请求伪造是一种网络安全漏洞,允许攻击者诱导用户执行他们不执行的操作 打算表演。

在您的情况下,禁用

CSRF 保护会使用户暴露于此漏洞。

注意:如果是带有 O-Auth 保护的纯 Rest API,则 CSRF 不是 需要。

我应该在 Rest API 端点上使用 CSRF 保护吗?

但是在您的情况下,当用户登录创建会话并返回 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 针对 REST 服务的 CSRF 保护:客户端和服务器端,以便更好地理解。


5
投票
这是一个简单的 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();

就能解决问题。


0
投票
适用于 Spring Security 6.2 及更高版本

在 http.build();之前

csrf().disable()

 -> 无法工作,因为 csrf 已贬值

http.csrf(AbstractHttpConfigurer::disable);
#添加这一行对我有用

返回http.build();

© www.soinside.com 2019 - 2024. All rights reserved.