108 votes

Comment configurer CORS dans une application Spring Boot + Spring Security ?

J'utilise Spring Boot avec Spring Security et Cors Support.

Si j'exécute le code suivant

url = 'http://localhost:5000/api/token'
xmlhttp = new XMLHttpRequest
xmlhttp.onreadystatechange = ->
    if xmlhttp.readyState is 4
        console.log xmlhttp.status
xmlhttp.open "GET", url, true
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:a'
do xmlhttp.send

J'obtiens comme résultat

200

Si je teste avec de mauvaises informations d'identification, par exemple

url = 'http://localhost:5000/api/token'
xmlhttp = new XMLHttpRequest
xmlhttp.onreadystatechange = ->
    if xmlhttp.readyState is 4
        console.log xmlhttp.status
xmlhttp.open "GET", url, true
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:aa'
do xmlhttp.send

Au lieu d'obtenir 401 (qui est le code standard pour une mauvaise authentification dans la sécurité de Spring), j'obtiens

0

avec la notification suivante du navigateur :

GET http://localhost:5000/api/token

XMLHttpRequest ne peut pas être chargé http://localhost:5000 . Aucun en-tête 'Access-Control-Allow-Origin' n'est présent sur la ressource demandée. Origine ' http://localhost:3000 L'accès est donc interdit. La réponse avait le code d'état HTTP 401.

Je développe un code frontal qui a besoin de codes d'état http utiles à partir des réponses du serveur pour gérer la situation. J'ai besoin de quelque chose de plus utile que 0. De plus, le corps de la réponse est vide. Je ne sais pas si ma configuration est mauvaise, ou si c'est un bug logiciel et je ne sais pas non plus où, si c'est chrome (utilisant arch linux) ou spring security.

Ma configuration de printemps est :

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

@RestController
@RequestMapping("api")
public class Controller {
    @RequestMapping("token")
    @CrossOrigin
    Map<String, String> token(HttpSession session) {
        return Collections.singletonMap("token", session.getId());
    }
}

@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("a").password("a").roles("USER");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .anyRequest().authenticated()
                .and().httpBasic();
    }
}

Si je teste avec curl tout fonctionne parfaitement, je pense que c'est parce qu'aucun support CORS n'est nécessaire, mais j'ai essayé de simuler le CORS avec des requêtes OPTION et le résultat était également correct.

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTpha"
*   Trying ::1...
* Connected to localhost (::1) port 5000 (#0)
> GET /api/token HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.48.0
> Accept: */*
> Authorization: Basic YTpha
> 
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Access-Control-Allow-Origin: http://localhost:3000
< Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE
< Access-Control-Max-Age: 3600
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: Origin,Accept,X-Requested-    With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization
< x-auth-token: 58e4cca9-7719-46c8-9180-2fc16aec8dff
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 01 May 2016 16:15:44 GMT
< 
* Connection #0 to host localhost left intact
{"token":"58e4cca9-7719-46c8-9180-2fc16aec8dff"}

et avec de mauvaises références :

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTp"
*   Trying ::1...
* Connected to localhost (::1) port 5000 (#0)
> GET /api/token HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.48.0
> Accept: */*
> Authorization: Basic YTp
> 
< HTTP/1.1 401 Unauthorized
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< WWW-Authenticate: Basic realm="Realm"
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 01 May 2016 16:16:15 GMT
< 
* Connection #0 to host localhost left intact
{"timestamp":1462119375041,"status":401,"error":"Unauthorized","message":"Failed to decode basic authentication token","path":"/api/token"}

Modifier : Pour éviter tout malentendu. J'utilise 1.3.3 Spring Boot. Le billet de blog écrit :

La prise en charge de CORS sera disponible dans la prochaine version Spring Boot 1.3, et est déjà disponible dans les builds 1.3.0.BUILD-SNAPSHOT.

L'utilisation de la configuration de la méthode CORS du contrôleur avec les annotations @CrossOrigin dans votre application Spring Boot ne nécessite aucune configuration spécifique.

La configuration globale de CORS peut être définie en enregistrant un bean WebMvcConfigurer avec une méthode personnalisée addCorsMappings(CorsRegistry) :

J'ai ajouté le code suivant pour activer le support global des cors. En fait, j'ai déjà essayé cela auparavant mais le résultat était le même. J'ai essayé à nouveau récemment et le résultat est le même.

@Configuration
public class MyConfiguration {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**");
            }
        };
    }
}

L'idée que le problème vient d'une redirection entre les processus d'autorisation est intéressante. Comment puis-je changer la redirection vers n'importe quelle ressource pour éviter ce conflit ?

EDITAR:

Je pense que je suis plus proche d'une solution. J'ai testé avec mon serveur nodejs qui supporte les cors sans problèmes en ajoutant Access-Control-Allow-Origin : * à toutes les requêtes.

Comme Stefan Isele l'a déjà mentionné, il semble que Spring Security redirige ou n'ajoute pas l'en-tête CORS, c'est pourquoi la requête semble être cassée. Donc, pendant que Spring Security vérifie l'authentification, il doit ajouter le bon header.

Quelqu'un sait-il comment faire ?

EDITAR:

J'ai trouvé une solution de contournement, qui semble être laide. J'ai commencé un problème github pour spring boot où je décris la solution de rechange : https://github.com/spring-projects/spring-boot/issues/5834

0 votes

stackoverflow.com/a/35040051/2884309 <- cette solution fonctionne toujours sur spring-boot 1.5.6

0 votes

Cela fonctionne-t-il aussi pour la demande PUT ou devons-nous le définir séparément ? Par défaut, toutes les origines et les méthodes GET, HEAD et POST sont autorisées. Extrait de la documentation de Spring ^^

95voto

Spring Security peut désormais tirer parti de la prise en charge de Spring MVC CORS décrite dans cet article de blog J'ai écrit.

Pour que cela fonctionne, vous devez explicitement activer le support CORS au niveau de Spring Security comme suit, sinon les requêtes CORS peuvent être bloquées par Spring Security avant d'atteindre Spring MVC.

Si vous utilisez le niveau de contrôleur @CrossOrigin il suffit d'activer le support CORS de Spring Security pour que la configuration de Spring MVC soit prise en compte :

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()...
    }
}

Si vous préférez utiliser la configuration globale de CORS, vous pouvez déclarer un fichier CorsConfigurationSource haricot comme suit :

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }
}

Cette approche remplace l'approche approche par filtre recommandé précédemment.

Vous pouvez trouver plus de détails dans le section dédiée à CORS de la documentation de Spring Security.

2 votes

J'ai fait en sorte que CORS+Spring Security fonctionne en utilisant un fichier CorsFilter haricot. Pour une raison quelconque, le FilterRegistrationBean n'a pas été récupéré.

0 votes

Bonjour, j'ai ceci : <mvc:cors> <mvc:mapping path="/rest/client/**" allowed-headers=" " allowed-methods="GET,PUT,HEAD,OPTIONS,POST,DELETE,PATCH" allowed-origins=" " allow-credentials="true" /> </mvc:cors> dans mon dispatcher-servlet. et ceci : <cors /> dans mon security.xml. Mais lorsque j'active l'authentification, cela ne fonctionne pas. Il n'y a pas de journalisation. Des indices ?

0 votes

En ajoutant que si vous utilisez une @RepositoryRestResource, vous devez l'activer spécifiquement, d'une autre manière : stackoverflow.com/questions/31724994/

79voto

zhouji Points 1050

Si vous utilisez le JDK 8+, il existe une solution lambda en une ligne :

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
}

0 votes

Une réponse "copier-coller" qui fonctionne du premier coup. cela fonctionne très bien merci

1 votes

Que fait-il exactement ? La requête du client javascript ne se plaindra plus du "blocage par la politique CORS" ?

0 votes

J'ai résolu le problème de la même exception CORS lorsque je faisais des requêtes POST avec Axios.

42voto

Je Suis Alrick Points 384

Si vous utilisez Sécurité du printemps vous pouvez procéder comme suit pour vous assurer que les demandes CORS sont traitées en premier :

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // by default uses a Bean by the name of corsConfigurationSource
            .cors().and()
            ...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

Voir Spring 4.2.x CORS pour plus d'informations.

Sans La sécurité du printemps, ça va marcher :

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS");
        }
    };
}

1 votes

J'ai copié la solution de codes appelée "Sans Spring Security" dans mon fichier d'application principal mais cela n'a pas fonctionné :(

0 votes

Aucun d'entre eux n'a fonctionné pour moi. Je continue à obtenir 401 avec des demandes d'option.

0 votes

Fonctionne parfaitement ! Il est important de se rappeler que (comme vous l'avez dit) si vous utilisez Spring Security, vous devez utiliser le premier extrait de code pour vous assurer que les requêtes CORS sont traitées en premier... C'était la clé dans mon cas. Merci beaucoup !

13voto

La protection contre l'origine croisée est une fonctionnalité du navigateur. Curl ne se soucie pas de CORS, comme vous l'avez présumé. Cela explique pourquoi vos curls sont réussis, alors que les demandes du navigateur ne le sont pas.

Si vous envoyez la requête du navigateur avec des informations d'identification erronées, spring tentera de renvoyer le client vers une page de connexion. Cette réponse (hors page de connexion) ne contient pas l'en-tête 'Access-Control-Allow-Origin' et le navigateur réagit comme vous le décrivez.

Vous devez faire en sorte d'inclure le haeder pour cette réponse de connexion, et peut-être pour d'autres réponses, comme les pages d'erreur, etc.

Cela peut être fait comme suit :

    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {

            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                    .allowedOrigins("http://domain2.com")
                    .allowedMethods("PUT", "DELETE")
                    .allowedHeaders("header1", "header2", "header3")
                    .exposedHeaders("header1", "header2")
                    .allowCredentials(false).maxAge(3600);
            }
     }

Ceci est copié de cors-support-in-spring-framework

Je commencerais par ajouter le mappage des cors pour toutes les ressources avec :

registry.addMapping("/**")

et autorisant également toutes les méthodes d'en-tête. Une fois que cela aura fonctionné, vous pourrez commencer à réduire à nouveau ce nombre au minimum nécessaire.

Veuillez noter que la configuration CORS change avec la version 4.2.

Si cela ne résout pas votre problème, postez la réponse que vous obtenez de la requête ajax qui a échoué.

1voto

Radouane ROUFID Points 6485

Pour la configuration des propriétés

# ENDPOINTS CORS CONFIGURATION (EndpointCorsProperties)
endpoints.cors.allow-credentials= # Set whether credentials are supported. When not set, credentials are not supported.
endpoints.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers.
endpoints.cors.allowed-methods=GET # Comma-separated list of methods to allow. '*' allows all methods.
endpoints.cors.allowed-origins= # Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled.
endpoints.cors.exposed-headers= # Comma-separated list of headers to include in a response.
endpoints.cors.max-age=1800 # How long, in seconds, the response from a pre-flight request can be cached by clients.

9 votes

Ces propriétés ne concernent que l'actionneur de Spring et ne s'appliquent pas à tous les points d'extrémité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X