Bota Angular / Spring con Keycloak lanza 403
Aseguré mi aplicación Spring Boot con Keycloak 11.0.2 y Spring Security siguiendo esta documentación .
Usé la configuración básica de Keycloak en application.properties:
keycloak.auth-server-url=http://localhost:8085/auth
keycloak.realm=cirta
keycloak.resource=cirta-api
keycloak.public-client=false
Tengo una aplicación Angular de interfaz separada, que está configurada como un cliente diferente en Keylocak; pero en el mismo ámbito que la aplicación Spring Boot. Desde la aplicación Angular, estoy enviando el token proporcionado por Keycloak en los encabezados HTTP con:
'Authorization' : 'Bearer ' + this.securityService.kc.token
Cuando accedo a una página angular que llama a una GETAPI, aparece un blocked by CORS policyerror:
Access to XMLHttpRequest at 'http://localhost:8080/api/modePaiements' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
Así que intenté agregar la keycloak.cors=truepropiedad a application.properties. Con esa propiedad agregada, las GETllamadas están funcionando. Pero ahora, cuando llamo a una POST/PUTAPI, aparece un Failed to load resource: the server responded with a status of 403 ()error.
KeycloakWebSecurityConfigurerAdapter:
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll();
}
Aplicación de muestra de primavera: https://github.com/bilaldekar/kc
Aplicación de muestra angular: https://github.com/bilaldekar/kc-ang
Solicitar encabezados:
Respuestas
Me parece un problema de CSRF.
Agregue lo siguiente a la configuración de seguridad
http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll()
.and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
El código anterior establecerá el token CSRF en la cookie nombrada XSRF-TOKEN. Angular lee esta cookie para tokens CSRF de forma predeterminada y la agrega en las solicitudes posteriores. Para asegurarnos de que Angular pueda leerlo, estamos configurando el Http onlyto False. Esto tiene sus propias implicaciones de seguridad, ya que ahora se puede acceder al token CSRF a través de un script. Si no prefiere hacer esto, la otra forma sería leer el X-XSRF-TOKENtoken del encabezado de respuesta y enviarlo en el encabezado de solicitud posterior utilizando el interceptor angular.
Actualizar:
Cuando se realiza una prueba local, donde Angular se ejecuta en 4200, la aplicación Spring Boot, el servidor Keycloak en los puertos 8080, 8085.
Utilice el proxy del servidor Webpack Dev con los siguientes pasos. (Ya no necesitas esta configuración keycloak.cors=true)
en la environment.tsactualización elapiUrl
apiUrl: '', //Spring Boot API
luego agregue un proxy.conf.jsondebajo de la srccarpeta con el siguiente contenido
{
"/api": {
"target": "http://localhost:8080",
"secure": false
},
"logLevel": "debug"
}
Agregue la configuración del proxy en el angular.jsoncomando de servicio
"options": {
"browserTarget": "kc-ang:build",
"proxyConfig": "src/proxy.conf.json"
}
Ahora notaría que las solicitudes de la API irían a localhost: 4200 / api / * y que se enrutarían a la aplicación Spring Boot a través de la configuración anterior.
Con esto, las cookies relacionadas con XSRF y el encabezado serían parte de la solicitud y no debería tener ningún problema.
[! [Versión de trabajo de muestra] [1]] [1]
Actualización 2: Soporte CORS
Si su frontend y backend están en dominios diferentes y si debe admitir CORS, entonces esto es lo que debe hacerse
La configuración de seguridad en Spring debería permitir CORS para el origen de la interfaz (en este caso http: // localhost: 4200):
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest()
.permitAll()
.and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and().cors();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
configuration.setAllowCredentials(Boolean.TRUE);
configuration.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/*", configuration);
return source;
}
En Angular, actualice apiUrl en el environment.tsarchivo a
apiUrl: '//localhost:8085'
Mientras realiza cualquier operación de escritura (POST / PUT / DELETE) usando el cliente http de Angular, configure la withCredentialsopción en true. (También asegúrese de que el método sea compatible con CORS en la configuración del lado del servidor. En la configuración de CORS anterior, solo permitimos GET y POST. Si necesita admitir PUT / DELETE, asegúrese de actualizar la configuración de CORS en consecuencia)
En este caso, actualice el siguiente método en fournisseur.service.ts
addFournisseur(fournisseur: Fournisseur): Observable<Fournisseur> {
return this.httpClient.post<Fournisseur>(this.envUrl + '/api/fournisseurs', fournisseur, {withCredentials: true});
}