如何忽略 spring 引导项目中特定 URL 的安全 CSRF

how to ignore spring security CSRF for specific URL's in spring boot project

如何忽略特定 URL 的 CSRF 安全性,例如“/workflow/**”。 除了这个 URL,我需要所有 URL 和方法的授权和 CSRF 安全。

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private RESTAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private RESTAuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private RESTAuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private PranaUserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().requireCsrfProtectionMatcher(new AllExceptUrlStartedWith("/workflow"))          
        .and().authorizeRequests()
        .antMatchers("/rest/**", "/tasklist").authenticated()
        .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
        .logoutSuccessUrl("/index.html")        
        .and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
        .and().formLogin().successHandler(authenticationSuccessHandler)
        .and().formLogin().failureHandler(authenticationFailureHandler)         
        .and().csrf().csrfTokenRepository(csrfTokenRepository()).and().addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
    }

    private static class AllExceptUrlStartedWith implements RequestMatcher {

        private static final String[] ALLOWED_METHODS =
                new String[] {"GET"};

        private final String[] allowedUrls;

        public AllExceptUrlStartedWith(String... allowedUrls) {
            this.allowedUrls = allowedUrls;
        }

        @Override
        public boolean matches(HttpServletRequest request) {
            String method = request.getMethod();
            for(String allowedMethod : ALLOWED_METHODS) {
                if (allowedMethod.equals(method)) {
                    return false;
                }
            }

            String uri = request.getRequestURI();
            for (String allowedUrl : allowedUrls) {
                if (uri.startsWith(allowedUrl)) {
                    return false;
                }
            }
            return true;
        }

    }

    private CsrfTokenRepository csrfTokenRepository() {
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName("X-XSRF-TOKEN");
        return repository;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/styles/**").antMatchers("/scripts/**");
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }
}

如何忽略特定 URL 的 CSRF 安全性,例如“/workflow/**”。 除了这个 URL,我需要所有 URL 和方法的授权和 CSRF 安全。

在我的项目中,我使用了以下代码:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        ...
        .csrf()
            // Allow unsecured requests to H2 console
            .requireCsrfProtectionMatcher(new AllExceptUrlsStartedWith("/console"))
        ...
}

private static class AllExceptUrlsStartedWith implements RequestMatcher {

        private static final String[] ALLOWED_METHODS =
            new String[] {"GET", "HEAD", "TRACE", "OPTIONS"};

        private final String[] allowedUrls;

        public AllExceptUrlsStartedWith(String... allowedUrls) {
            this.allowedUrls = allowedUrls;
        }

        @Override
        public boolean matches(HttpServletRequest request) {
            // replicate default behavior (see CsrfFilter.DefaultRequiresCsrfMatcher class)
            String method = request.getMethod();
            for (String allowedMethod : ALLOWED_METHODS) {
                if (allowedMethod.equals(method)) {
                    return false;
                }
            }

            // apply our own exceptions
            String uri = request.getRequestURI();
            for (String allowedUrl : allowedUrls) {
                if (uri.startsWith(allowedUrl)) {
                    return false;
                }
            }

            return true;
        }

    }

在此示例中,我为 /console 禁用了 CSRF 保护。


更新:since Spring Security 4.0 you can simplify it to a single line

csrf()
    .ignoringAntMatchers("/nocsrf","/ignore/startswith/**")

回答我自己的问题...感谢@Slava

@Configuration
    @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
    protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {

        @Autowired
        private RESTAuthenticationEntryPoint authenticationEntryPoint;

        @Autowired
        private RESTAuthenticationFailureHandler authenticationFailureHandler;

        @Autowired
        private RESTAuthenticationSuccessHandler authenticationSuccessHandler;

        @Autowired
        private PranaUserDetailsService userDetailsService;

        @Override
        protected void configure(HttpSecurity http) throws Exception {

            http.csrf().requireCsrfProtectionMatcher(new AllExceptUrlStartedWith("/workflow"))          
            .and().authorizeRequests()
            .antMatchers("/rest/**", "/tasklist").authenticated()
            .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/index.html")        
            .and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
            .and().formLogin().successHandler(authenticationSuccessHandler)
            .and().formLogin().failureHandler(authenticationFailureHandler)         
            .and().csrf().csrfTokenRepository(csrfTokenRepository()).and().addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
        }

        private static class AllExceptUrlStartedWith implements RequestMatcher {

            private static final String[] ALLOWED_METHODS =
                    new String[] {"GET"};

            private final String[] allowedUrls;

            public AllExceptUrlStartedWith(String... allowedUrls) {
                this.allowedUrls = allowedUrls;
            }

            @Override
            public boolean matches(HttpServletRequest request) {
                String method = request.getMethod();
                for(String allowedMethod : ALLOWED_METHODS) {
                    if (allowedMethod.equals(method)) {
                        return false;
                    }
                }

                String uri = request.getRequestURI();
                for (String allowedUrl : allowedUrls) {
                    if (uri.startsWith(allowedUrl)) {
                        return false;
                    }
                }
                return true;
            }

        }

        private CsrfTokenRepository csrfTokenRepository() {
            HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
            repository.setHeaderName("X-XSRF-TOKEN");
            return repository;
        }

        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/styles/**").antMatchers("/scripts/**");
        }

        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
        }
    }

在这个帖子里回答的唯一目的就是解释和使用antPathMatcher,其优点可以用来保护许多带有蚂蚁匹配器的url。

来自文档

.csrf().requireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher)


Specify the RequestMatcher to use for determining when CSRF should be applied. The default is to ignore GET, HEAD, TRACE, OPTIONS and process all other requests.

请注意,默认情况下 GETHEADTRACEOPTIONS 请求将被忽略。如果你想覆盖这个默认配置 requireCsrfProtectionMatcher(implementation_of_RequestMatcher)

在 RequestMatcher 的实现中定义所有需要保护的 URL。 你完成了

假设您希望 URL 的 /api/** 得到 CSRF 保护.

@Autowired
RequestMatcher csrfProtectedMatchers;

@Override
protected void configure(final HttpSecurity http) throws Exception
{
    http
        .authorizeRequests()
            .antMatchers("/resources/**", "/", "/login").permitAll()
            .antMatchers("/api/**").hasAnyRole("ADMIN", "USER")
            .antMatchers("/app/user/*")
                .hasAnyRole("ADMIN", "USER")
        .and().formLogin()
        .and().csrf().requireCsrfProtectionMatcher(csrfProtectedMatchers);
}

@Bean
public RequestMatcher getCsrfProtectedMatchers()
{
    UrlPathHelper urlPathHelper = new UrlPathHelper();
    AntPathMatcher antPathMatcher = new AntPathMatcher();

    List<String> protectedUrlPatterns = Arrays.asList("/api/**", "/logout");

    return new RequestMatcher()
    {
        @Override
        public boolean matches(HttpServletRequest request)
        {
            String uri = urlPathHelper.getPathWithinApplication(request);
            for (String pattern : protectedUrlPatterns)
            {
                if (antPathMatcher.match(pattern, uri))
                {
                    return true;
                }
            }
            return false;
        }
    };
}

逻辑解释
假设 URL: http://localhost:8080/csrf/api/test1
String uri = urlPathHelper.getPathWithinApplication(request);
uri => /api/test1;
antPathMatcher.match("/api/**", "/api/test1") => true