Angular / Spring Boot พร้อม Keycloak พ่น 403
ฉันรักษาความปลอดภัยแอปพลิเคชัน 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
ขอส่วนหัว:
คำตอบ
ดูเหมือนว่าฉันจะมีปัญหา 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});
}