如何在 Spring Boot 项目中忽略特定 URL 的 Spring Security CSRF

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

如何忽略特定 URL(如“/workflow/**”)的 CSRF 安全性。 除了这个 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(如“/workflow/**”)的 CSRF 安全性。 除了这个 URL 之外,我还需要所有 URL 和方法的授权和 CSRF 安全性。

java spring-boot spring-security csrf rest-client
4个回答
7
投票

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

@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 保护。


更新:从 Spring Security 4.0 开始,您可以将其简化为一行

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

1
投票

在这个帖子中回答的唯一目的是解释和使用

antPathMatcher
,它的优点可以利用蚂蚁匹配器来保护许多网址。

来自医生

.csrf().requireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher)


指定用于确定何时应应用 CSRF 的 RequestMatcher。默认是忽略 GET、HEAD、TRACE、OPTIONS 并处理所有其他请求。

请注意,默认情况下

GET
HEAD
TRACE
OPTIONS
请求将被忽略。如果您想覆盖此默认设置,请配置
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;
        }
    };
}

逻辑解释
假设网址:

http://localhost:8080/csrf/api/test1

String uri = urlPathHelper.getPathWithinApplication(request);

uri =>
/api/test1
;
antPathMatcher.match("/api/**", "/api/test1")
=>
true


0
投票

回答我自己的问题...感谢@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());
        }
    }

0
投票

在 Spring Security 6 中,可以使用

CsrfConfigurer#ignoringRequestMatchers

例如:

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.csrf(csrf -> csrf.ignoringRequestMatchers("/workflow/**"));
    return http.build();
}
© www.soinside.com 2019 - 2024. All rights reserved.