J'ai créé deux applications Web - les applications client et service.
L'interaction entre les applications client et les applications de service se passe bien lorsqu'elles sont déployées dans la même instance Tomcat.
Mais lorsque les applications sont déployées dans des instances distinctes de Tomcat (machines différentes), j'obtiens l'erreur ci-dessous lors de la demande d'envoi de l'application de service.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401
L'application My Client utilise JQuery, HTML5 et Bootstrap.
Un appel AJAX est effectué pour le service comme indiqué ci-dessous:
var auth = "Basic " + btoa({usname} + ":" + {password});
var service_url = {serviceAppDomainName}/services;
if($("#registrationForm").valid()){
var formData = JSON.stringify(getFormData(registrationForm));
$.ajax({
url: service_url+action,
dataType: 'json',
async: false,
type: 'POST',
headers:{
"Authorization":auth
},
contentType: 'application/json',
data: formData,
success: function(data){
//success code
},
error: function( jqXhr, textStatus, errorThrown ){
alert( errorThrown );
});
}
Mon application de service utilise Spring MVC, Spring Data JPA et Spring Security.
J'ai inclus la classe CorsConfiguration
comme indiqué ci-dessous:
CORSConfig.Java
:
@Configuration
@EnableWebMvc
public class CORSConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("*");
}
}
SecurityConfig.Java
:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@ComponentScan(basePackages = "com.services", scopedProxy = ScopedProxyMode.INTERFACES)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("authenticationService")
private UserDetailsService userDetailsService;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().fullyAuthenticated();
http.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
}
Dépendances de Spring Security:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
J'utilise Apache Tomcat server pour le déploiement.
La requête de contrôle en amont de CORS utilise HTTP OPTIONS
sans informations d'identification, voir Partage de ressources d'origine croisée :
Sinon, faites une demande preflight. Récupérez l'URL de la demande à partir de la source d'origine Origin en utilisant la source de référent comme source de référent avec l'indicateur de redirection manuelle et l'indicateur de cookies de blocage définis, à l'aide de la méthode OPTIONS et avec les contraintes supplémentaires suivantes:
- Incluez un en-tête Access-Control-Request-Method avec comme champ la valeur de la méthode request (même s’il s’agit d’une méthode simple).
- Si les en-têtes de demande d’auteur ne sont pas vides, incluez un en-tête Access-Control-Request-Headers avec comme valeur de champ d’en-tête une liste séparée par des virgules des noms de champ d’en-tête des en-têtes de demande d’auteur dans l’ordre lexicographique, chacun converti en minuscule ASCII même quand un ou plusieurs sont un simple en-tête).
- Exclure les en-têtes de demande d’auteur.
- Exclure les informations d'identification de l'utilisateur.
- Exclure le corps d'entité de la demande.
Vous devez autoriser l'accès anonyme pour HTTP OPTIONS
.
Votre code modifié (et simplifié):
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.andMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable();
}
Depuis Spring Security 4.2.0, vous pouvez utiliser le support intégré, voir Référence de sécurité Spring :
19. CORS
Spring Framework fournit un support de première classe pour CORS. La CORS doit être traitée avant la sécurité Spring, car la demande de pré-vol ne contiendra aucun cookie (c.-à-d. La
JSESSIONID
). Si la demande ne contient pas de cookies et que Spring Security est le premier, la demande déterminera que l'utilisateur n'est pas authentifié (car il n'y a pas de cookies dans la demande) et la rejettera.Le moyen le plus simple de s’assurer que CORS est traité en premier est d’utiliser la variable
CorsFilter
. Les utilisateurs peuvent intégrer laCorsFilter
à Spring Security en fournissant uneCorsConfigurationSource
en utilisant les éléments suivants:@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; } }
Dans mon cas, le serveur de ressources avec la sécurité OAuth est activé et les solutions ci-dessus ne fonctionnent pas. Après un peu de débogage et googler compris pourquoi.
@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(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
Fondamentalement, dans cet exemple, Ordered.HIGHEST_PRECEDENCE
est la clé!
https://github.com/spring-projects/spring-security-oauth/issues/938
Différentes dépendances de pom ajoutent différents types de filtres et par conséquent, nous pourrions avoir des problèmes liés à l'ordre.