尽管有SecurityConfig

时间:2018-06-25 15:09:45

标签: spring-boot spring-security

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

通过使用本地数据库来完成认证,该本地数据库包含具有两组不同角色的用户:ADMINUSERUSER应该能够GET所有API端点,并且POST到基于routeA的端点。 ADMIN应该能够对基于`routeB

的端点进行USER加上POSTDELETE的相同操作

无论如何,我得到的行为是我可以向任何终结点发出GET请求,但对于任何类型的用户POSTHTTP 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();
    }
}

3 个答案:

答案 0 :(得分:7)

SecurityConfiguration.java中有2个问题使其行为异常。

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

{
    "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

答案 1 :(得分:1)

跨站点请求伪造是一个网络安全漏洞,攻击者可以利用该漏洞诱使用户执行他们不执行的操作 打算表演。

就您而言,禁用 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);
    }
}

标题响应表服务器: enter image description here

请注意,我们现在在标头中有CSRF令牌。在会话期满之前,这不会改变。 另请阅读:Spring Security’s CSRF protection for REST services: the client side and the server side,以更好地理解。

答案 2 :(得分:0)

这是启用了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();

将解决问题。