web-dev-qa-db-fra.com

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

Je reçois à la suite

200

Si je teste avec des informations d'identification erronées comme

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 (c’est le code standard pour une mauvaise authentification dans la sécurité du printemps), j’obtiens

0

avec la notification suivante du navigateur:

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

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

Je développe un code frontal qui nécessite des codes d’état http utiles provenant des réponses du serveur pour gérer la situation. J'ai besoin de quelque chose de plus utile que 0. Le corps de la réponse est également vide. Je ne sais pas si ma configuration est fausse ou s'il s'agit d'un bogue logiciel et je ne sais pas non plus où, si c'est du chrome (avec Arch Linux) ou une sécurité à ressort.

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, car aucun support CORS n’est nécessaire, mais j’ai essayé de simuler 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 des informations d'identification erronées:

$ 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"}

Edit: Pour éviter les malentendus. J'utilise 1.3.3 Spring Boot. L'article de blog écrit:

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

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

La configuration globale 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 cors. En fait, j'ai déjà essayé cela auparavant, mais le résultat était le même. Je l'ai réessayé 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 le processus d'autorisation est intéressante. Comment puis-je changer la redirection vers des ressources pour éviter ce conflit?

MODIFIER:

Je suppose que je suis plus proche d'une solution. J'ai testé avec mon serveur nodejs qui prend en charge cors sans problème en ajoutant Access-Control-Allow-Origin: * à toutes les demandes.

Comme Stefan Isele l’a déjà mentionné, il semble que la sécurité de printemps redirige ou n’ajoute pas l’en-tête CORS, c’est pourquoi la demande semble être interrompue. Ainsi, pendant que Spring Security vérifie l'authentification, il doit ajouter l'en-tête approprié.

Est-ce que quelqu'un sait comment faire?

MODIFIER:

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

43
Rudolf Schmidt

Spring Security peut maintenant tirer parti du support Spring MVC CORS décrit dans cet article de blog j'ai écrit.

Pour que cela fonctionne, vous devez activer explicitement la prise en charge de CORS au niveau de Spring Security, sinon les demandes activées par CORS risquent d'être bloquées par Spring Security avant d'atteindre Spring MVC.

Si vous utilisez des annotations @CrossOrigin de niveau de contrôleur, il vous suffit d'activer la prise en charge de Spring Security CORS, qui utilisera la configuration Spring MVC:

@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 CORS, vous pouvez déclarer un bean CorsConfigurationSource 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 la approche basée sur le filtre précédemment recommandée.

Vous trouverez plus de détails dans la section dédiée de CORS de la documentation Spring Security.

34

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

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

Si vous utilisez Spring Security , 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 Spring Security cela fonctionnera:

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

La protection d’origine croisée est une fonctionnalité du navigateur. Curl ne se soucie pas de la SCRO, comme vous l'avez présumé. Cela explique pourquoi vos boucles réussissent, contrairement aux requêtes du navigateur.

Si vous envoyez la demande du navigateur avec les informations d'identification incorrectes, spring essaiera de transférer le client vers une page de connexion. Cette réponse (en dehors de la 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 un printemps pour inclure le haeder pour cette réponse de connexion, et éventuellement pour une autre réponse, telle que des pages d'erreur, etc.

Cela peut être fait comme ça:

    @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é à partir de cors-support-in-spring-framework

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

registry.addMapping("/**")

et autorisant également toutes les méthodes d'en-têtes. Une fois que cela fonctionne, vous pouvez commencer à le réduire au minimum nécessaire.

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

Si cela ne résout pas vos problèmes, postez la réponse obtenue à partir de la demande ajax ayant échoué.

Cors peut être pénible, mais avec ce code simple, vous êtes Cors SEULEMENT !!!! to à la méthode spécifiée

@CrossOrigin(origins="*")// in this line add your url and thats is all for spring boot side
    @GetMapping("/some")
    public String index() {
        return "pawned cors!!!!";
    }

Comme un charme dans les bottes de printemps 2.0.2

3
Andres Navarro

Nous avons trouvé une solution simple pour la configuration basée sur Java: Spring-Boot, Spring-Security et:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.cors().configurationSource(new CorsConfigurationSource() {
            @Override
            public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                return new CorsConfiguration().applyPermitDefaultValues();
            }
        });
    }
}
3

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.
2
Radouane ROUFID

J'ai résolu ce problème en: `

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("*"));
    configuration.setAllowCredentials(true);
    configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers","Access-Control-Allow-Origin","Access-Control-Request-Method", "Access-Control-Request-Headers","Origin","Cache-Control", "Content-Type", "Authorization"));
    configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

`

2
Toan Truong

J'ai résolu ce problème par:

@Configuration
public class CORSFilter extends CorsFilter {

    public CORSFilter(CorsConfigurationSource source) {
        super((CorsConfigurationSource) source);
    }

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

        response.addHeader("Access-Control-Allow-Headers",
                "Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
        if (response.getHeader("Access-Control-Allow-Origin") == null)
            response.addHeader("Access-Control-Allow-Origin", "*");
        filterChain.doFilter(request, response);
    }

}

et:

@Configuration
public class RestConfig {

    @Bean
    public CORSFilter corsFilter() {
        CorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("http://localhost:4200");
        config.addAllowedMethod(HttpMethod.DELETE);
        config.addAllowedMethod(HttpMethod.GET);
        config.addAllowedMethod(HttpMethod.OPTIONS);
        config.addAllowedMethod(HttpMethod.PUT);
        config.addAllowedMethod(HttpMethod.POST);
        ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", config);
        return new CORSFilter(source);
    }
}
2
Özlem Gürses

J'ai eu le même problème sur un méthood qui renvoie le statut du serveur. L'application est déployée sur plusieurs serveurs. Donc, le plus facile que j'ai trouvé est d'ajouter

@CrossOrigin(origins = "*")
@RequestMapping(value="/schedulerActive")
public String isSchedulerActive(){
  //code goes here
}

Cette méthode n'est pas sécurisée mais vous pouvez ajouter allowCredentials pour cela.

1
Khalifa

Après une longue recherche de l'erreur provenant de javascript CORS, la seule solution élégante que j'ai trouvée pour cette affaire consistait à configurer les cors de la classe de classe org.springframework.web.cors.CorsConfiguration.CorsConfiguration () de Spring.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

J'avais de gros problèmes avec Axios, Spring Boot et Spring Security avec authentification.

Veuillez noter la version de Spring Boot et la sécurité de printemps que vous utilisez, ça compte.

Botte de printemps: 1.5.10 Spring: 4.3.14 Sécurité de printemps 4.2.4

Pour résoudre ce problème à l'aide de la configuration Java par annotation, j'ai créé la classe suivante:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

        auth.inMemoryAuthentication()
                .withUser("youruser").password("yourpassword")
                .authorities("ROLE_USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.cors().and().
                authorizeRequests()
                .requestMatchers(CorsUtils:: isPreFlightRequest).permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .httpBasic()
                .realmName("Biometrix");

        http.csrf().disable();

    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Arrays.asList("Authorization"));
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

L'un des principaux pièges avec Axios est que lorsque votre API requiert une authentification, elle envoie un en-tête Authorization avec la requête OPTIONS. Si vous n'incluez pas l'autorisation dans le paramètre de configuration des en-têtes autorisés, notre demande OPTIONS (également appelée demande PreFlight) échouera et Axios signalera une erreur.

Comme vous pouvez le voir avec quelques paramètres simples et correctement placés, la configuration de CORS avec SpringBoot est assez facile.

0
Sami Elhini

Solution de Kotlin

...
http.cors().configurationSource {
  CorsConfiguration().applyPermitDefaultValues()
}
...
0
Ahmed H. Saab