如何在spring boot项目中忽略特定URL的spring security CSRF

Posted

技术标签:

【中文标题】如何在spring boot项目中忽略特定URL的spring security CSRF【英文标题】:how to ignore spring security CSRF for specific URL's in spring boot project 【发布时间】:2015-12-18 08:45:29 【问题描述】:

如何忽略特定 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 安全。

【问题讨论】:

【参考方案1】:

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

@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:它不适合我! /login 和 /workflow/** 都出现 403 禁止错误......有什么帮助吗?? 所以,请在某处发布您更新的 Spring 安全配置(例如 pastebin)。 谢谢斯拉瓦。实际上这是我的错误,由于我公司的特定依赖关系而出错。它正在工作,现在我为“/workflow”调用禁用了 CSRF,并为所有其他休息调用启用了 CSRF 再次感谢您的帮助 使用antPathMatcher.match() 将生成用于定义诸如/console/** 之类的通配符URL 的最少代码。您可以在我的回答中参考。【参考方案2】:

在此线程中回答的唯一目的是解释和使用antPathMatcher,它的优点可以利用蚂蚁匹配器保护许多网址。

来自文档

.csrf().requireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher)


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

请注意,默认情况下 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;
        
    ;

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

【讨论】:

【参考方案3】:

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

【讨论】:

以上是关于如何在spring boot项目中忽略特定URL的spring security CSRF的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Spring Boot @RepositoryRestResource 映射到特定的 url?

如何在使用 Spring 会话的项目中设置忽略 URL

如何将Spring Boot RepositoryRestResource映射到特定的URL

在 spring-boot 中禁用特定 url 的 Keycloak 身份验证

在aws上忽略基于url到spring boot 2应用程序的黑客攻击是不是安全?

如何查看spring boot url mapping