Angular / Spring Boot dengan Keycloak melempar 403

Sep 23 2020

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

GSSwain Oct 13 2020 at 12:39

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});
  }