it-swarm.com.ru

Пружина безопасности CORS Filter

Мы добавили Spring Security в наш существующий проект. 

С этого момента мы получаем ошибку 401 No 'Access-Control-Allow-Origin' header is present on the requested resource с нашего сервера. 

Это потому, что к ответу не прикреплен заголовок Access-Control-Allow-Origin. Чтобы исправить это, мы добавили наш собственный фильтр, который находится в цепочке Filter перед фильтром выхода из системы, но этот фильтр не применяется для наших запросов.

Наша ошибка:

XMLHttpRequest не может загрузить http://localhost:8080/getKunden. В запрошенном ресурсе отсутствует заголовок «Access-Control-Allow-Origin». Исходный код http://localhost:3000 поэтому запрещен. Ответ имел HTTP-код состояния 401.

Наша конфигурация безопасности:

@EnableWebSecurity
@Configuration
@ComponentScan("com.company.praktikant")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
private MyFilter filter;

@Override
public void configure(HttpSecurity http) throws Exception {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();

    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("GET");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("POST");
    source.registerCorsConfiguration("/**", config);
    http.addFilterBefore(new MyFilter(), LogoutFilter.class).authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS, "/*").permitAll();
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
}
}

Наш фильтр

@Component
public class MyFilter extends OncePerRequestFilter {

@Override
public void destroy() {

}

private String getAllowedDomainsRegex() {
    return "individual / customized Regex";
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    final String Origin = "http://localhost:3000";

    response.addHeader("Access-Control-Allow-Origin", Origin);
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
    response.setHeader("Access-Control-Allow-Credentials", "true");
    response.setHeader("Access-Control-Allow-Headers",
            "content-type, x-gwt-module-base, x-gwt-permutation, clientid, longpush");

    filterChain.doFilter(request, response);

}
}

Наше приложение

@SpringBootApplication
public class Application {
public static void main(String[] args) {
    final ApplicationContext ctx = SpringApplication.run(Application.class, args);
    final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(CORSConfig.class);
    annotationConfigApplicationContext.refresh();
}
}

Наш фильтр зарегистрирован с весны-загрузки:

2016-11-04 09: 19: 51.494 INFO 9704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean: Фильтр отображения: 'myFilter' на: [/ *]

Наша созданная цепочка фильтров:

2016-11-04 09: 19: 52.729 INFO 9704 --- [ost-startStop-1] ossweb.DefaultSecurityFilterChain: создание цепочки фильтров: [email protected]1, [org. springframework.secu[email protected]5d8c5a8a, org.spring[email protected]7d6938f, org.springfurity.weg.serg.raf [email protected], [email protected], org.[email protected]2330834f, org.springframework.security.qug. springframework.[email protected]4fc0f1a2, org.springframework.security.web.authentication. Exce ptionTranslationFilter @ 4b8bf1fb, org.springfr[email protected]42063cf1]

Ответ: Заголовки ответа

Мы пробовали решение еще с весны, но оно не сработало! Аннотация @CrossOrigin в нашем контроллере тоже не помогла.

Правка 1:

Пробовал решение от @Piotr Sołtysiak. Фильтр cors не указан в сгенерированной цепочке фильтров, и мы все еще получаем ту же ошибку.

2016-11-04 10: 22: 49.881 INFO 8820 --- [ost-startStop-1] ossweb.DefaultSecurityFilterChain: создание цепочки фильтров: [email protected]1, [org. springframework.secu[email protected]4c191377, org.spring[email protected]28bad32a, org.springframeworkrit.raf. [email protected], org.[email protected]1c9cd096, org.springframework.s[email protected]3workme.secre1, wee.aut.wef DefaultLoginPageGeneratingFilter @ 1e8d4ac1, org.springfram[email protected]2d61d2a4, org.springframework.security.web.savedrequest.RequestCacheAwareFilsec.web.web.dll rAwareRequestFilter @ abf2de3, org.springfram[email protected]2a5c161b, o[email protected]3work70eweg.file.prg. [email protected]]

Кстати, мы используем Spring-Security версии 4.1.3.!

15
M. Thiele

Хорошо, после 2 дней поиска мы наконец исправили проблему. Мы удалили все наши фильтры и конфигурации и вместо этого использовали эти 5 строк кода в классе приложения.

@SpringBootApplication
public class Application {
public static void main(String[] args) {
    final ApplicationContext ctx = SpringApplication.run(Application.class, args);
}

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedOrigins("http://localhost:3000");
        }
    };
}
}
11
M. Thiele

Начиная с Spring Security 4.1, это правильный способ обеспечить поддержку Spring Security CORS (также необходимо в Spring Boot 1.4/1.5):

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
    }
}

а также:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.csrf().disable();
        http.cors();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(ImmutableList.of("*"));
        configuration.setAllowedMethods(ImmutableList.of("HEAD",
                "GET", "POST", "PUT", "DELETE", "PATCH"));
        // setAllowCredentials(true) is important, otherwise:
        // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
        configuration.setAllowCredentials(true);
        // setAllowedHeaders is important! Without it, OPTIONS preflight request
        // will fail with 403 Invalid CORS request
        configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

Не делайте not, выполнив одно из следующих действий, которые являются неправильным способом решения проблемы:

  • http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
  • web.ignoring().antMatchers(HttpMethod.OPTIONS);

Ссылка: http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html

46
Hendy Irawan

Поскольку у меня были проблемы с другими решениями (особенно для того, чтобы он работал во всех браузерах, например, Edge не распознает «*» в качестве допустимого значения для «Access-Control-Allow-Methods»), мне пришлось использовать пользовательский Компонент фильтра, который в итоге сработал для меня и сделал именно то, чего я хотел достичь.

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods",
                "ACL, CANCELUPLOAD, CHECKIN, CHECKOUT, COPY, DELETE, GET, HEAD, LOCK, MKCALENDAR, MKCOL, MOVE, OPTIONS, POST, PROPFIND, PROPPATCH, PUT, REPORT, SEARCH, UNCHECKOUT, UNLOCK, UPDATE, VERSION-CONTROL");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers",
                "Origin, X-Requested-With, Content-Type, Accept, Key, Authorization");

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }

    public void init(FilterConfig filterConfig) {
        // not needed
    }

    public void destroy() {
        //not needed
    }

}
8
David Eibensteiner
  1. Вам не нужно:

    @Configuration
    @ComponentScan("com.company.praktikant")
    

    В @EnableWebSecurity уже есть @Configuration, и я не могу представить, почему вы положили @ComponentScan туда.

  2. О фильтре CORS я бы просто сказал:

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0); 
        return bean;
    }
    

    В класс SecurityConfiguration и удалите настройку и настройте глобальные методы. Вам не нужно устанавливать разрешающие организации, заголовки и методы дважды. Особенно, если вы установите разные свойства в конфигурации фильтра и безопасности весны :)

  3. В соответствии с вышесказанным, ваш класс "MyFilter" является избыточным.

  4. Вы также можете удалить те:

    final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(CORSConfig.class);
    annotationConfigApplicationContext.refresh();
    

    Из класса приложений.

  5. В конце небольшой совет - не связан с вопросом. Вы не хотите помещать глаголы в URI. Вместо http://localhost:8080/getKunden вы должны использовать метод HTTP GET для ресурса http://localhost:8080/kunden. Вы можете узнать о лучших методах разработки RESTful API здесь: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

7
Piotr Sołtysiak

Согласно документация фильтра CORS :

«Spring MVC обеспечивает детальную поддержку конфигурации CORS Посредством аннотаций на контроллерах. Однако при использовании с Spring Security рекомендуется полагаться на встроенный CorsFilter, который необходимо упорядочить впереди сети фильтров Spring Security "

Нечто подобное позволит GET доступ к /ajaxUri:

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import Java.util.Arrays;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AjaxCorsFilter extends CorsFilter {
    public AjaxCorsFilter() {
        super(configurationSource());
    }

    private static UrlBasedCorsConfigurationSource configurationSource() {
        CorsConfiguration config = new CorsConfiguration();

        // origins
        config.addAllowedOrigin("*");

        // when using ajax: withCredentials: true, we require exact Origin match
        config.setAllowCredentials(true);

        // headers
        config.addAllowedHeader("x-requested-with");

        // methods
        config.addAllowedMethod(HttpMethod.OPTIONS);
        config.addAllowedMethod(HttpMethod.GET);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/startAsyncAuthorize", config);
        source.registerCorsConfiguration("/ajaxUri", config);
        return source;
    }
}

Конечно, ваша конфигурация SpringSecurity должна разрешать доступ к URI перечисленными методами. Смотрите @Hendy Irawan ответ.

3
lmiguelmh

Во многих местах я вижу ответ, который должен добавить этот код: 

@Bean
public FilterRegistrationBean corsFilter() {
   UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
   CorsConfiguration config = new CorsConfiguration();
   config.setAllowCredentials(true);
   config.addAllowedOrigin("*");
   config.addAllowedHeader("*");
   config.addAllowedMethod("*");
   source.registerCorsConfiguration("/**", config);
   FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
   bean.setOrder(0); 
   return bean;
}

но в моем случае он выдает неожиданное исключение типа класса. Для бина corsFilter() требуется тип CorsFilter, поэтому я внес эти изменения и поместил это определение бина в мою конфигурацию, и теперь все в порядке.

@Bean
public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}
2
Garik Kalashyan

В моем случае я просто добавил этот класс и использую @EnableAutConfiguration

package com.package.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import Java.io.IOException;


 @Component
 public class SimpleCORSFilter extends GenericFilterBean {

/**
 * The Logger for this class.
 */
private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Override
public void doFilter(ServletRequest req, ServletResponse resp,
                     FilterChain chain) throws IOException, ServletException {
    logger.info("> doFilter");

    HttpServletResponse response = (HttpServletResponse) resp;
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
    //response.setHeader("Access-Control-Allow-Credentials", "true");
    chain.doFilter(req, resp);

    logger.info("< doFilter");
}

}

2
Alex Fernandez

Для программ, которые уже развернуты и не могут позволить себе изменения кода (например, добавить/обновить безопасность Spring), добавление простого прокси-сервера является одним из решений: https://stackoverflow.com/a/49827300/1758194

0
Orvyl