Angular / Spring Boot พร้อม Keycloak พ่น 403

Sep 23 2020

ฉันรักษาความปลอดภัยแอปพลิเคชัน Spring Boot ด้วย Keycloak 11.0.2 และ Spring Security ตามเอกสารนี้

ฉันใช้การกำหนดค่า Keycloak พื้นฐานในapplication.properties:

    keycloak.auth-server-url=http://localhost:8085/auth
    keycloak.realm=cirta
    keycloak.resource=cirta-api
    keycloak.public-client=false

ฉันมีแอป Angular ส่วนหน้าแยกต่างหากซึ่งกำหนดค่าเป็นไคลเอนต์อื่นใน Keylocak แต่อยู่ในขอบเขตเดียวกับแอป Spring Boot จากแอป Angular ฉันกำลังส่งโทเค็นที่ให้โดย Keycloak ในส่วนหัว HTTP ด้วย:

'Authorization' : 'Bearer ' + this.securityService.kc.token

เมื่อฉันเข้าถึงเพจ Angular ที่เรียกGETAPI ฉันได้รับblocked by CORS policyข้อผิดพลาด:

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.

ฉันได้ลองเพิ่มkeycloak.cors=trueคุณสมบัติเป็นapplication.propertiesแล้ว เมื่อเพิ่มคุณสมบัติดังกล่าวการGETโทรกำลังทำงาน แต่ตอนนี้เมื่อฉันเรียกPOST/PUTAPI ฉันได้รับFailed to load resource: the server responded with a status of 403 ()ข้อผิดพลาด

Keycloak WebSecurityConfigurer อะแดปเตอร์:

@Override
protected void configure(HttpSecurity http) throws Exception {
    super.configure(http);
    http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll();
}

การใช้งานตัวอย่างสปริง: https://github.com/bilaldekar/kc

แอปพลิเคชันตัวอย่างเชิงมุม: https://github.com/bilaldekar/kc-ang

ขอส่วนหัว:

คำตอบ

GSSwain Oct 13 2020 at 12:39

ดูเหมือนว่าฉันจะมีปัญหา csrf

เพิ่มสิ่งต่อไปนี้ในการกำหนดค่าความปลอดภัย

http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll()
                .and().csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

รหัสข้างต้นจะตั้ง CSRF XSRF-TOKENโทเค็นในคุกกี้ที่มีชื่อ Angular อ่านคุกกี้นี้สำหรับโทเค็น CSRF ตามค่าเริ่มต้นและเพิ่มสิ่งนี้ในคำขอที่ตามมา เพื่อให้แน่ใจว่าเชิงมุมสามารถอ่านได้เรามีการตั้งค่าที่จะHttp only Falseสิ่งนี้มีผลกระทบด้านความปลอดภัยของตัวเองเนื่องจากโทเค็น CSRF สามารถเข้าถึงได้ผ่านสคริปต์ในขณะนี้ หากคุณไม่ต้องการทำเช่นนี้อีกวิธีหนึ่งคือการอ่านX-XSRF-TOKENโทเค็นจากส่วนหัวการตอบกลับและส่งไปยังส่วนหัวของคำขอที่ตามมาโดยใช้ Angular interceptor

อัปเดต:

เมื่อทำการทดสอบภายในเครื่องโดยที่ Angular ทำงานบน 4200 แอป Spring Boot เซิร์ฟเวอร์ Keycloak บนพอร์ต 8080, 8085

ใช้พร็อกซีเซิร์ฟเวอร์ Webpack Dev ตามขั้นตอนต่อไปนี้ (คุณไม่ต้องการการกำหนดค่านี้อีกต่อไปkeycloak.cors=true)

ในการenvironment.tsอัปเดตไฟล์apiUrl

apiUrl: '', //Spring Boot API

จากนั้นเพิ่มproxy.conf.jsonภายใต้srcโฟลเดอร์ที่มีเนื้อหาต่อไปนี้

{
    "/api": {
        "target": "http://localhost:8080",
        "secure": false
    },
    "logLevel": "debug"
}

เพิ่มการกำหนดค่าพร็อกซีในangular.jsonคำสั่ง serve

"options": {
    "browserTarget": "kc-ang:build",
    "proxyConfig": "src/proxy.conf.json"
}

ตอนนี้คุณจะสังเกตเห็นว่าคำขอ api จะไปที่ localhost: 4200 / api / * และจะถูกส่งไปยังแอป spring boot ผ่านการกำหนดค่าด้านบน

ด้วยคุกกี้ที่เกี่ยวข้องกับ XSRF และส่วนหัวจะเป็นส่วนหนึ่งของคำขอและคุณไม่ควรได้รับปัญหาใด ๆ

[! [เวอร์ชันการทำงานตัวอย่าง] [1]] [1]

อัปเดต 2: สนับสนุน CORS

หากส่วนหน้าและส่วนหลังของคุณอยู่ในโดเมนที่แตกต่างกันและหากคุณต้องรองรับ CORS นี่คือสิ่งที่ต้องทำ

การกำหนดค่าความปลอดภัยบน Spring ควรอนุญาต CORS สำหรับจุดเริ่มต้นของส่วนหน้า (ในกรณีนี้คือ 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;
}

บน Angular ให้อัพเดต apiUrl ในenvironment.tsไฟล์เป็น

apiUrl: '//localhost:8085'

ในขณะที่การดำเนินการเขียนใด ๆ (โพสต์ / PUT / ลบ) ใช้ไคลเอ็นต์ http เชิงมุมตั้งค่าตัวเลือกในการwithCredentials true(ตรวจสอบให้แน่ใจว่าวิธีนี้ได้รับการสนับสนุนสำหรับ CORS ในการกำหนดค่าฝั่งเซิร์ฟเวอร์ในการกำหนดค่า CORS ด้านบนเราอนุญาตให้ GET และ POST เท่านั้นหากคุณต้องการรองรับ PUT / DELETE ให้แน่ใจว่าคุณอัปเดตการกำหนดค่า CORS ตามนั้น)

ในกรณีนี้ให้อัพเดตวิธีการต่อไปนี้ใน fournisseur.service.ts

addFournisseur(fournisseur: Fournisseur): Observable<Fournisseur> {
    return this.httpClient.post<Fournisseur>(this.envUrl + '/api/fournisseurs', fournisseur, {withCredentials: true});
  }