web-dev-qa-db-fra.com

Filtre de sécurité CORS à ressort

Nous avons ajouté Spring Security à notre projet existant. 

À partir de ce moment, nous obtenons une erreur 401 No 'Access-Control-Allow-Origin' header is present on the requested resource de la part de notre serveur. 

En effet, aucun en-tête Access-Control-Allow-Origin n'est associé à la réponse. Pour résoudre ce problème, nous avons ajouté notre propre filtre qui se trouve dans la chaîne Filter avant le filtre de déconnexion, mais le filtre ne s'applique pas à nos demandes.

Notre erreur:

XMLHttpRequest ne peut pas charger http://localhost:8080/getKunden. Aucun en-tête 'Access-Control-Allow-Origin' n'est présent sur la ressource demandée. Origin http://localhost:3000 n'est donc pas autorisé à accéder. La réponse avait le code d'état HTTP 401.

Notre configuration de sécurité:

@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 {
}
}

Notre filtre

@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);

}
}

Notre application

@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();
}
}

Notre filtre est enregistré à partir de spring-boot:

2016-11-04 09: 19: 51.494 INFO 9704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean: Filtre de mappage: 'monFiltre' à: [/ *]

Notre chaîne de filtrage générée:

2016-11-04 09: 19: 52.729 INFO 9704 --- [ost-startStop-1] ossweb.DefaultSecurityFilterChain: Création d'une chaîne de filtres: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org. sprint web.csrf.CsrfFilter@4af4df11, com.company.praktikant.MyFil, springframework.security.web.sletletapi.SecurityContendolderAwareRequestFil@4Springframework.security.web.authentique Exce ptionTranslationFilter @ 4b8bf1fb, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@42063cf1]

La réponse: En-têtes de réponse

Nous avons essayé la solution dès le printemps également mais cela n’a pas fonctionné! L'annotation @CrossOrigin dans notre contrôleur n'a pas aidé non plus.

Modifier 1:

J'ai essayé la solution de @Piotr Sołtysiak. Le filtre cors n'est pas répertorié dans la chaîne de filtres générée et nous obtenons toujours la même erreur.

2016-11-04 10: 22: 49.881 INFO 8820 --- [ost-startStop-1] ossweb.DefaultSecurityFilterChain: Création d'une chaîne de filtres: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org. leproductreframework web.csrf.CsrfFilter@288460dd, org.springframework.security.web.authentication.logout.LogoutFilter@1c9cd096, DefaultLoginPageGeneratingFilter @ 1e8d41, surproduction security.web.access.intercept.FilterSecurityInterceptor@5d27725a]

Au fait, nous utilisons Spring-security version 4.1.3.!

15
M. Thiele

Ok, après plus de 2 jours de recherche, nous avons finalement résolu le problème. Nous avons supprimé tous nos filtres et configurations et utilisé à la place ces 5 lignes de code dans la classe d'application.

@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

Depuis que j'ai eu des problèmes avec les autres solutions (en particulier pour le faire fonctionner dans tous les navigateurs, par exemple, Edge ne reconnaît pas "*" comme une valeur valide pour "Access-Control-Allow-Methods"), j'ai dû utiliser un paramètre personnalisé. La composante de filtre, qui à la fin a fonctionné pour moi et a fait exactement ce que je voulais réaliser.

@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. Vous n'avez pas besoin de:

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

    @EnableWebSecurity contient déjà @Configuration, et je ne vois pas pourquoi vous y avez mis @ComponentScan.

  2. À propos du filtre CORS, je voudrais juste dire ceci:

    @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;
    }
    

    Dans la classe SecurityConfiguration et supprimez la configuration et configurez les méthodes globales. Vous n'avez pas besoin de définir deux fois les orgins autorisés, les en-têtes et les méthodes. Surtout si vous mettez des propriétés différentes dans le filtre et la configuration de sécurité du ressort :)

  3. Selon ce qui précède, votre classe "MyFilter" est redondante.

  4. Vous pouvez également supprimer ceux-ci:

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

    De la classe d'application.

  5. À la fin, petit conseil - pas lié à la question. Vous ne voulez pas mettre de verbes dans l'URI. Au lieu de http://localhost:8080/getKunden, vous devez utiliser la méthode HTTP GET sur la ressource http://localhost:8080/kunden. Vous pouvez en apprendre davantage sur les meilleures pratiques de conception RESTful api ici: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

7
Piotr Sołtysiak

Selon la documentation du filtre CORS :

"Spring MVC fournit un support détaillé pour la configuration CORS Via des annotations sur les contrôleurs. Cependant lorsqu'il est utilisé avec Spring Security, il est conseillé de s’appuyer sur le CorsFilter intégré qui doit être commandé avant la chaîne de filtres de Spring Security "

Quelque chose comme ceci permettra à GET d'accéder au /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;
    }
}

Bien entendu, votre configuration SpringSecurity doit autoriser l'accès à l'URI avec les méthodes répertoriées. Voir la réponse de @Hendy Irawan.

3
lmiguelmh

Dans de nombreux endroits, je vois la réponse qui doit ajouter ce code: 

@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;
}

mais dans mon cas, il lève une exception de type classe inattendue. corsFilter() bean nécessite CorsFilter type, donc j'ai apporté ces modifications et mis cette définition de bean dans ma configuration et tout va bien maintenant.

@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

Dans mon cas, je viens d'ajouter cette classe et d'utiliser @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

Pour les programmes déjà déployés et ne pouvant pas se permettre de modifications de code (sécurité E.gg add/update), l'ajout d'un proxy simple est une solution: https://stackoverflow.com/a/49827300/1758194

0
Orvyl