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 ^^