Angular / Spring Boot dengan Keycloak melempar 403
Saya mengamankan aplikasi Spring Boot saya dengan Keycloak 11.0.2 dan Spring Security mengikuti dokumentasi ini .
Saya menggunakan konfigurasi Keycloak dasar di application.properties:
keycloak.auth-server-url=http://localhost:8085/auth
keycloak.realm=cirta
keycloak.resource=cirta-api
keycloak.public-client=false
Saya memiliki aplikasi Angular frontend terpisah, yang dikonfigurasi sebagai klien berbeda di Keylocak; tetapi di ranah yang sama dengan aplikasi Spring Boot. Dari aplikasi Angular, saya mengirimkan token yang disediakan Keycloak di header HTTP dengan:
'Authorization' : 'Bearer ' + this.securityService.kc.token
Ketika saya mengakses halaman Angular yang memanggil GETAPI, saya mendapatkan blocked by CORS policykesalahan:
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.
Jadi saya sudah mencoba menambahkan keycloak.cors=trueproperti ke application.properties. Dengan properti itu ditambahkan, GETpanggilan bekerja. Tapi sekarang ketika saya memanggil POST/PUTAPI saya mendapatkan Failed to load resource: the server responded with a status of 403 ()kesalahan.
KeycloakWebSecurityConfigurerAdapter:
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll();
}
Aplikasi Sampel Musim Semi: https://github.com/bilaldekar/kc
Aplikasi Sampel Sudut: https://github.com/bilaldekar/kc-ang
Minta Header:
Jawaban
Menurut saya masalah csrf.
Tambahkan berikut ini ke konfigurasi keamanan
http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll()
.and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
Kode di atas akan mengatur token CSRF dalam cookie bernama XSRF-TOKEN. Angular membaca cookie ini untuk token CSRF secara default dan menambahkan ini di permintaan berikutnya. Untuk memastikan Angular dapat membacanya, kami menyetel Http onlyke False. Ini memiliki implikasi keamanannya sendiri karena token CSRF dapat diakses melalui skrip sekarang. Jika Anda tidak memilih untuk melakukan ini, cara lain adalah membaca X-XSRF-TOKENtoken dari header respons dan mengirimkannya ke header permintaan berikutnya menggunakan interseptor Angular.
Memperbarui:
Saat menguji secara lokal, di mana Angular berjalan pada 4200, aplikasi Spring Boot, server Keycloak pada port 8080, 8085.
Gunakan proxy server Webpack Dev dengan langkah-langkah berikut. (Anda tidak membutuhkan konfigurasi ini lagi keycloak.cors=true)
dalam environment.tspembaruanapiUrl
apiUrl: '', //Spring Boot API
lalu tambahkan di proxy.conf.jsonbawah srcfolder dengan konten berikut
{
"/api": {
"target": "http://localhost:8080",
"secure": false
},
"logLevel": "debug"
}
Tambahkan konfigurasi proxy di angular.jsonperintah servis
"options": {
"browserTarget": "kc-ang:build",
"proxyConfig": "src/proxy.conf.json"
}
Sekarang Anda akan melihat permintaan api akan pergi ke localhost: 4200 / api / * dan itu akan diarahkan ke aplikasi boot musim semi melalui konfigurasi di atas.
Dengan ini cookie terkait XSRF dan header akan menjadi bagian dari permintaan dan Anda seharusnya tidak mendapatkan masalah apa pun.
[! [Contoh versi kerja] [1]] [1]
Pembaruan 2: Dukung CORS
Jika frontend dan backend Anda berada di domain yang berbeda dan jika Anda harus mendukung CORS, inilah yang perlu dilakukan
Konfigurasi keamanan di Spring harus mengizinkan CORS untuk asal frontend (dalam hal ini 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;
}
Di Angular, perbarui apiUrl dalam environment.tsfile menjadi
apiUrl: '//localhost:8085'
Saat melakukan operasi tulis (POST / PUT / DELETE) menggunakan klien http Angular, setel withCredentialsopsi ke true. (Pastikan juga metode ini didukung untuk CORS pada konfigurasi sisi server. Dalam konfigurasi CORS di atas, kami hanya mengizinkan GET dan POST. Jika Anda perlu mendukung PUT / DELETE, pastikan Anda memperbarui konfigurasi CORS yang sesuai)
Dalam hal ini perbarui metode berikut di fournisseur.service.ts
addFournisseur(fournisseur: Fournisseur): Observable<Fournisseur> {
return this.httpClient.post<Fournisseur>(this.envUrl + '/api/fournisseurs', fournisseur, {withCredentials: true});
}