尽管有SecurityConfig,Spring Security还是阻止了POST请求

Tia*_*ite 11 spring-security spring-boot

我正在开发基于Spring Boot(spring-boot-starter-web)的REST API,其中使用Spring Security(spring-security-coree spring-security-config)保护不同的端点。

身份验证是通过使用本地数据库完成的,该本地数据库包含具有两个不同角色集的用户:ADMINUSERUSER应该能够访问GET所有API端点以及POST基于的端点routeAADMIN应该能够与基于routeB的USERplus POSTDELETEend相同

但是,我得到的行为是,我可以GET向任何端点发出请求,但是对于任何类型的用户,POST请求总是返回HTTP 403 Forbidden- ADMINUSER-这不是我期望的基于我的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;
    }
Run Code Online (Sandbox Code Playgroud)

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!";
    }

}
Run Code Online (Sandbox Code Playgroud)

RESTAuthenticationEntryPoint.java

@Component
public class RESTAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {

    @Override
    public void afterPropertiesSet() throws Exception {
        setRealmName("AppNameHere");
        super.afterPropertiesSet();
    }
}
Run Code Online (Sandbox Code Playgroud)

Tia*_*ite 15

有2个问题SecurityConfiguration.java使其无法正常运行。

尽管该403 Forbidden错误消息未包含任何指示其失败原因的消息(请参见下面的示例),但事实证明,这是由于启用了CSRF而导致的。禁用它允许POSTDELETE要求进行处理。

{
    "timestamp": "2018-06-26T09:17:19.672+0000",
    "status": 403,
    "error": "Forbidden",
    "message": "Forbidden",
    "path": "/routeB"
}
Run Code Online (Sandbox Code Playgroud)

另外,antMatched(HttpMethod, String)for RouteB中使用的表达式也是错误的,因为/routeB/*期望它在after之后有一些东西/。正确的配置是/routeB/**因为可以存在(或不存在)更多路径。


修正 SecurityConfiguration.java IS

@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();
}
Run Code Online (Sandbox Code Playgroud)

资料来源: StackOverflow emPortuguês

  • CSRF...唷!谢谢! (5认同)

shu*_*1js 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.**
}
Run Code Online (Sandbox Code Playgroud)

上面的代码:

http.csrf().disable();
Run Code Online (Sandbox Code Playgroud)

将解决问题。


Moh*_*eem 5

\n

跨站点请求伪造是一种 Web 安全漏洞,攻击者可以利用该漏洞诱导用户执行他们不打算执行的操作。

\n
\n

在您的情况下,禁用CSRF保护会使用户面临此漏洞。

\n
\n

注意:如果它是带有 O-Auth 保护的纯 Rest API,则不需要 CSRF。我应该在 Rest API 端点上使用 CSRF 保护吗?\n

\n
\n

但是在您的情况下,当用户登录时创建会话并返回 cookie 作为响应,并且没有CSRF令牌 攻击者可以利用它并执行CSRF

\n

禁用 CSRF 不是一个好主意,相反,您可以将应用程序配置为在响应标头中返回 CSRF 令牌,然后在所有后续状态更改调用中使用它。

\n

在您的SecurityConfiguration.java中添加这行代码

\n
// CSRF tokens handling\nhttp.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);\n
Run Code Online (Sandbox Code Playgroud)\n

CsrfTokenResponseHeaderBindingFilter.java

\n
public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter {\n    protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";\n    protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";\n    protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";\n    protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException {\n        CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);\n\n        if (token != null) {\n            response.setHeader(RESPONSE_HEADER_NAME, token.getHeaderName());\n            response.setHeader(RESPONSE_PARAM_NAME, token.getParameterName());\n            response.setHeader(RESPONSE_TOKEN_NAME, token.getToken());\n        }\n\n        filterChain.doFilter(request, response);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

来自服务器的标头响应:\n在此输入图像描述

\n

请注意,我们现在标头中有 CSRF 令牌。在会话过期之前,这不会改变。\n另请阅读:Spring Security\xe2\x80\x99s REST 服务的 CSRF 保护:客户端和服务器端,以便更好地理解。

\n

  • (其他)请务必使用正确的导入:`org.springframework.security.web.csrf.CsrfToken`,否则您可能会得到:`class org.springframework.security.web.csrf.DefaultCsrfToken无法转换为类org.springframework .security.web.server.csrf.CsrfToken` (2认同)