Angular / Spring Boot với Keycloak ném 403
Tôi đã bảo mật ứng dụng Spring Boot của mình bằng Keycloak 11.0.2 và Spring Security theo tài liệu này .
Tôi đã sử dụng cấu hình Keycloak cơ bản trong application.properties:
keycloak.auth-server-url=http://localhost:8085/auth
keycloak.realm=cirta
keycloak.resource=cirta-api
keycloak.public-client=false
Tôi có một ứng dụng Angular frontend riêng biệt, được định cấu hình như một ứng dụng khách khác trong Keylocak; nhưng trong cùng lĩnh vực với ứng dụng Spring Boot. Từ ứng dụng Angular, tôi đang gửi mã thông báo do Keycloak cung cấp trong tiêu đề HTTP với:
'Authorization' : 'Bearer ' + this.securityService.kc.token
Khi tôi truy cập một trang Angular gọi GETAPI, tôi gặp blocked by CORS policylỗi:
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.
Vì vậy, tôi đã thử thêm thuộc keycloak.cors=truetính vào application.properties. Với thuộc tính đó được thêm vào, các GETcuộc gọi đang hoạt động. Nhưng bây giờ khi tôi gọi một POST/PUTAPI, tôi gặp Failed to load resource: the server responded with a status of 403 ()lỗi.
KeycloakWebSecurityConfigurerAdapter:
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll();
}
Ứng dụng mẫu mùa xuân: https://github.com/bilaldekar/kc
Ứng dụng mẫu góc: https://github.com/bilaldekar/kc-ang
Yêu cầu Tiêu đề:
Trả lời
Tôi gặp vấn đề về csrf.
Thêm phần sau vào cấu hình bảo mật
http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll()
.and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
Đoạn mã trên sẽ đặt mã thông báo CSRF trong cookie được đặt tên XSRF-TOKEN. Angular đọc cookie này cho các mã thông báo CSRF theo mặc định và thêm nó vào các yêu cầu tiếp theo. Để đảm bảo Angular có thể đọc nó, chúng tôi đang đặt Http onlythành False. Điều này có ý nghĩa bảo mật riêng vì mã thông báo CSRF hiện có thể được truy cập thông qua tập lệnh. Nếu bạn không thích làm điều này, cách khác sẽ là đọc X-XSRF-TOKENmã thông báo từ tiêu đề phản hồi và gửi nó trong tiêu đề yêu cầu tiếp theo bằng cách sử dụng Angular interceptor.
Cập nhật:
Khi thử nghiệm cục bộ, nơi Angular đang chạy trên 4200, ứng dụng Spring Boot, máy chủ Keycloak trên cổng 8080, 8085.
Sử dụng proxy máy chủ Webpack Dev theo các bước sau. (Bạn không cần cấu hình này nữa keycloak.cors=true)
trong environment.tsbản cập nhậtapiUrl
apiUrl: '', //Spring Boot API
sau đó thêm một proxy.conf.jsondưới srcthư mục với nội dung sau
{
"/api": {
"target": "http://localhost:8080",
"secure": false
},
"logLevel": "debug"
}
Thêm cấu hình proxy trong angular.jsonlệnh phục vụ
"options": {
"browserTarget": "kc-ang:build",
"proxyConfig": "src/proxy.conf.json"
}
Bây giờ bạn sẽ thấy các yêu cầu api sẽ chuyển đến localhost: 4200 / api / * và điều đó sẽ được chuyển đến ứng dụng khởi động mùa xuân thông qua cấu hình trên.
Với điều này, cookie liên quan đến XSRF và tiêu đề sẽ là một phần của yêu cầu và bạn sẽ không gặp bất kỳ vấn đề nào.
[! [Phiên bản làm việc mẫu] [1]] [1]
Cập nhật 2: Hỗ trợ CORS
Nếu giao diện người dùng và chương trình phụ trợ của bạn nằm trên các miền khác nhau và nếu bạn phải hỗ trợ CORS, thì đây là điều cần phải làm
Cấu hình bảo mật trên Spring phải cho phép CORS cho nguồn gốc của giao diện người dùng (trong trường hợp này là 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;
}
Trên Angular, cập nhật apiUrl trong environment.tstệp thành
apiUrl: '//localhost:8085'
Trong khi thực hiện bất kỳ thao tác ghi nào (POST / PUT / DELETE) bằng máy khách Angular http, hãy đặt withCredentialstùy chọn thành true. (Đồng thời đảm bảo phương thức được hỗ trợ cho CORS trên cấu hình phía máy chủ. Trong cấu hình CORS trên, chúng tôi chỉ cho phép GET và ĐĂNG. Nếu bạn cần hỗ trợ PUT / DELETE, hãy đảm bảo bạn cập nhật cấu hình CORS cho phù hợp)
Trong trường hợp này, hãy cập nhật phương pháp sau trong fournisseur.service.ts
addFournisseur(fournisseur: Fournisseur): Observable<Fournisseur> {
return this.httpClient.post<Fournisseur>(this.envUrl + '/api/fournisseurs', fournisseur, {withCredentials: true});
}