2016-05-01 47 views
24

Tôi sử dụng Spring Boot với Spring Security và Cors Support.Cách cấu hình CORS trong ứng dụng Spring Boot + Spring Security?

Nếu tôi thực hiện đoạn mã sau

url = 'http://localhost:5000/api/token' 
xmlhttp = new XMLHttpRequest 
xmlhttp.onreadystatechange = -> 
    if xmlhttp.readyState is 4 
     console.log xmlhttp.status 
xmlhttp.open "GET", url, true 
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest" 
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:a' 
do xmlhttp.send 

tôi nhận được kết quả

200 

một Nếu tôi thử nghiệm với các thông tin sai như

url = 'http://localhost:5000/api/token' 
xmlhttp = new XMLHttpRequest 
xmlhttp.onreadystatechange = -> 
    if xmlhttp.readyState is 4 
     console.log xmlhttp.status 
xmlhttp.open "GET", url, true 
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest" 
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:aa' 
do xmlhttp.send 

thay vì nhận được 401 (đó là mã chuẩn để xác thực sai trong bảo mật mùa xuân) Tôi nhận được

0 

với thông báo trình duyệt sau:

GET http://localhost:5000/api/token

XMLHttpRequest không thể tải http://localhost:5000. Không có tiêu đề 'Access-Control-Allow-Origin' xuất hiện trên tài nguyên được yêu cầu. Do đó, nguồn gốc 'http://localhost:3000' không được phép truy cập. Phản hồi có mã trạng thái HTTP 401.

Tôi đang phát triển mã front-end cần mã trạng thái http hữu ích từ các phản hồi của máy chủ để xử lý tình huống. Tôi cần một cái gì đó hữu ích hơn 0. Ngoài ra cơ thể phản ứng là trống rỗng. Tôi không biết nếu cấu hình của tôi là sai, hoặc đó là một lỗi phần mềm và tôi cũng không biết ở đâu, nếu đó là chromium (sử dụng Linux vòm) hoặc bảo mật mùa xuân.

My Xuân Config là:

@SpringBootApplication 
public class Application { 
    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 
} 

@RestController 
@RequestMapping("api") 
public class Controller { 
    @RequestMapping("token") 
    @CrossOrigin 
    Map<String, String> token(HttpSession session) { 
     return Collections.singletonMap("token", session.getId()); 
    } 
} 

@EnableWebSecurity 
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { 
    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
     auth.inMemoryAuthentication().withUser("a").password("a").roles("USER"); 
    } 
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
       .authorizeRequests() 
       .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() 
       .anyRequest().authenticated() 
       .and().httpBasic(); 
    } 
} 

Nếu tôi thử nghiệm với tất cả mọi thứ curl công trình hoàn hảo, tôi nghĩ rằng vì không CORS hỗ trợ cần thiết, nhưng tôi đã cố gắng để mô phỏng các CORS với yêu cầu OPTION và kết quả cũng ok.

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTpha" 
* Trying ::1... 
* Connected to localhost (::1) port 5000 (#0) 
> GET /api/token HTTP/1.1 
> Host: localhost:5000 
> User-Agent: curl/7.48.0 
> Accept: */* 
> Authorization: Basic YTpha 
> 
< HTTP/1.1 200 OK 
< Server: Apache-Coyote/1.1 
< X-Content-Type-Options: nosniff 
< X-XSS-Protection: 1; mode=block 
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate 
< Pragma: no-cache 
< Expires: 0 
< X-Frame-Options: DENY 
< Access-Control-Allow-Origin: http://localhost:3000 
< Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE 
< Access-Control-Max-Age: 3600 
< Access-Control-Allow-Credentials: true 
< Access-Control-Allow-Headers: Origin,Accept,X-Requested- With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization 
< x-auth-token: 58e4cca9-7719-46c8-9180-2fc16aec8dff 
< Content-Type: application/json;charset=UTF-8 
< Transfer-Encoding: chunked 
< Date: Sun, 01 May 2016 16:15:44 GMT 
< 
* Connection #0 to host localhost left intact 
{"token":"58e4cca9-7719-46c8-9180-2fc16aec8dff"} 

và với các thông tin sai:

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTp" 
* Trying ::1... 
* Connected to localhost (::1) port 5000 (#0) 
> GET /api/token HTTP/1.1 
> Host: localhost:5000 
> User-Agent: curl/7.48.0 
> Accept: */* 
> Authorization: Basic YTp 
> 
< HTTP/1.1 401 Unauthorized 
< Server: Apache-Coyote/1.1 
< X-Content-Type-Options: nosniff 
< X-XSS-Protection: 1; mode=block 
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate 
< Pragma: no-cache 
< Expires: 0 
< X-Frame-Options: DENY 
< WWW-Authenticate: Basic realm="Realm" 
< Content-Type: application/json;charset=UTF-8 
< Transfer-Encoding: chunked 
< Date: Sun, 01 May 2016 16:16:15 GMT 
< 
* Connection #0 to host localhost left intact 
{"timestamp":1462119375041,"status":401,"error":"Unauthorized","message":"Failed to decode basic authentication token","path":"/api/token"} 

Edit: Để tránh hiểu lầm. Tôi sử dụng 1.3.3 Spring Boot. Bài đăng trên blog viết:

Hỗ trợ CORS sẽ có trong bản phát hành Spring Boot 1.3 sắp tới và đã có sẵn trong bản 1.3.0.BUILD-SNAPSHOT.

Sử dụng phương pháp điều khiển Cấu hình CORS với chú thích @CrossOrigin trong ứng dụng Khởi động mùa xuân của bạn không yêu cầu bất kỳ cấu hình cụ thể nào.

cấu hình toàn cầu CORS thể được định nghĩa bằng cách đăng ký một bean WebMvcConfigurer với một addCorsMappings tùy chỉnh (CorsRegistry) Phương pháp:

Tôi đã thêm mã sau đây để kích hoạt tính năng hỗ trợ CORS toàn cầu. thực sự tôi đã thử điều này trước đây nhưng kết quả là như nhau. Tôi đã thử lại lần nữa và kết quả là như nhau.

@Configuration 
public class MyConfiguration { 

    @Bean 
    public WebMvcConfigurer corsConfigurer() { 
     return new WebMvcConfigurerAdapter() { 
      @Override 
      public void addCorsMappings(CorsRegistry registry) { 
       registry.addMapping("/**"); 
      } 
     }; 
    } 
} 

Ý tưởng, vấn đề xuất phát từ chuyển hướng giữa quá trình ủy quyền là một điều thú vị. làm cách nào tôi có thể thay đổi chuyển hướng sang bất kỳ tài nguyên nào để tránh xung đột này?

EDIT:

Tôi đoán tôi gần hơn với giải pháp. Tôi đã thử nghiệm với máy chủ nodejs của mình hỗ trợ cors mà không gặp sự cố bằng cách thêm Access-Control-Allow-Origin: * cho tất cả các yêu cầu.

Giống như Stefan Isele đã đề cập có vẻ như bảo mật mùa xuân chuyển hướng hoặc không thêm tiêu đề CORS vì vậy đó là lý do tại sao yêu cầu có vẻ bị hỏng. Vì vậy, trong khi bảo mật mùa xuân đang kiểm tra xác thực, nó phải thêm tiêu đề thích hợp.

Có ai biết cách làm như vậy không?

CHỈNH SỬA:

Tôi tìm thấy giải pháp thay thế, điều đó có vẻ xấu. Tôi đã bắt đầu một vấn đề github cho khởi động mùa xuân, nơi tôi mô tả cách giải quyết: https://github.com/spring-projects/spring-boot/issues/5834

+0

https://stackoverflow.com/a/35040051/2884309 <- giải pháp này vẫn hoạt động trên spring-boot 1.5.6 –

Trả lời

3

Bảo vệ gốc chéo là một tính năng của trình duyệt. Curl không quan tâm đến CORS, như bạn nghĩ. Điều đó giải thích tại sao các lọn tóc của bạn thành công, trong khi các yêu cầu của trình duyệt thì không.

Nếu bạn gửi yêu cầu trình duyệt với thông tin đăng nhập sai, spring sẽ cố chuyển tiếp ứng dụng đến trang đăng nhập. Phản hồi này (tắt trang đăng nhập) không chứa tiêu đề 'Access-Control-Allow-Origin' và trình duyệt phản ứng như bạn mô tả.

Bạn phải làm cho mùa xuân để bao gồm các haeder cho phản ứng đăng nhập này, và có thể cho phản ứng khác, như các trang báo lỗi, vv

Điều này có thể được thực hiện như thế này:

@Configuration 
    @EnableWebMvc 
    public class WebConfig extends WebMvcConfigurerAdapter { 

      @Override 
      public void addCorsMappings(CorsRegistry registry) { 
       registry.addMapping("/api/**") 
        .allowedOrigins("http://domain2.com") 
        .allowedMethods("PUT", "DELETE") 
        .allowedHeaders("header1", "header2", "header3") 
        .exposedHeaders("header1", "header2") 
        .allowCredentials(false).maxAge(3600); 
      } 
    } 

này được sao chép từ cors-support-in-spring-framework

tôi sẽ bắt đầu bằng cách thêm bản đồ CORS cho tất cả các tài nguyên với:

registry.addMapping("/**") 

và cũng cho phép tất cả các tiêu đề phương thức .. Khi nó hoạt động, bạn có thể bắt đầu giảm lại một lần nữa đến mức tối thiểu cần thiết.

Xin lưu ý rằng cấu hình CORS thay đổi với Bản phát hành 4.2.

Nếu cách này không giải quyết được sự cố của bạn, hãy đăng câu trả lời bạn nhận được từ yêu cầu ajax không thành công.

22

Bảo mật mùa xuân hiện có thể tận dụng hỗ trợ Spring MVC CORS được mô tả trong this blog post Tôi đã viết.

Để làm cho nó hoạt động, bạn cần bật hỗ trợ CORS một cách rõ ràng ở cấp độ Bảo mật mùa xuân như sau, nếu không yêu cầu CORS có thể bị chặn bởi Spring Security trước khi đến Spring MVC.

Nếu bạn đang sử dụng mức điều khiển @CrossOrigin chú thích, bạn chỉ cần phải kích hoạt hỗ trợ Xuân An CORS và nó sẽ tận dụng cấu hình Spring MVC:

@EnableWebSecurity 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.cors().and()... 
    } 
} 

Nếu bạn thích sử dụng CORS global configuration, bạn có thể khai báo một CorsConfigurationSource đậu như sau:

@EnableWebSecurity 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.cors().and()... 
    } 

    @Bean 
    CorsConfigurationSource corsConfigurationSource() { 
     UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 
     source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); 
     return source; 
    } 
} 

Cách tiếp cận này thay thế cho filter-based approach trước đây được đề nghị.

Bạn có thể tìm thêm chi tiết trong tài liệu dedicated CORS section của tài liệu bảo mật mùa xuân.

+0

Tôi đã tạo CORS + Spring Security bằng cách sử dụng bean 'CorsFilter'. Vì lý do nào đó, FilterRegistrationBean không được chọn. – jasonleakey

+0

hi, tôi có điều này: \t \t \t \t trong bộ điều phối-servlet của tôi. và điều này: \t \t trong security.xml của tôi. Nhưng khi tôi bật xác thực nó không hoạt động. Không có đăng nhập. Bất kì manh mối nào? – tibi

9

Nếu bạn đang sử dụng Xuân An, bạn có thể làm như sau để đảm bảo rằng các yêu cầu CORS được xử lý đầu tiên:

@EnableWebSecurity 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
      // by default uses a Bean by the name of corsConfigurationSource 
      .cors().and() 
      ... 
    } 

    @Bean 
    CorsConfigurationSource corsConfigurationSource() { 
     CorsConfiguration configuration = new CorsConfiguration(); 
     configuration.setAllowedOrigins(Arrays.asList("https://example.com")); 
     configuration.setAllowedMethods(Arrays.asList("GET","POST")); 
     UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 
     source.registerCorsConfiguration("/**", configuration); 
     return source; 
    } 
} 

Xem http://docs.spring.io/spring-security/site/docs/current/reference/html/cors.html để biết thêm thông tin. cấu hình

2

Đối với tài sản cấu hình

# ENDPOINTS CORS CONFIGURATION (EndpointCorsProperties) 
endpoints.cors.allow-credentials= # Set whether credentials are supported. When not set, credentials are not supported. 
endpoints.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers. 
endpoints.cors.allowed-methods=GET # Comma-separated list of methods to allow. '*' allows all methods. 
endpoints.cors.allowed-origins= # Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled. 
endpoints.cors.exposed-headers= # Comma-separated list of headers to include in a response. 
endpoints.cors.max-age=1800 # How long, in seconds, the response from a pre-flight request can be cached by clients. 
+2

Các thuộc tính này chỉ dành cho Thiết bị truyền động mùa xuân và không áp dụng cho tất cả các điểm cuối. – His

0

Tìm thấy một giải pháp dễ dàng cho mùa xuân-Boot, Spring-An ninh và Java dựa trên:

@Configuration 
@EnableWebSecurity 
@EnableGlobalMethodSecurity(prePostEnabled = true) 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(HttpSecurity httpSecurity) throws Exception { 
     httpSecurity.cors().configurationSource(new CorsConfigurationSource() { 
      @Override 
      public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { 
       return new CorsConfiguration().applyPermitDefaultValues(); 
      } 
     }); 
    } 
} 
3

Nếu bạn sử dụng JDK 8+, có một giải pháp lambda dòng:

@EnableWebSecurity 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues()); 
} 
0

Tôi giải quyết vấn đề này theo:

@Configuration 
public class CORSFilter extends CorsFilter { 

    public CORSFilter(CorsConfigurationSource source) { 
     super((CorsConfigurationSource) source); 
    } 

    @Override 
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 
      throws ServletException, IOException { 

     response.addHeader("Access-Control-Allow-Headers", 
       "Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); 
     if (response.getHeader("Access-Control-Allow-Origin") == null) 
      response.addHeader("Access-Control-Allow-Origin", "*"); 
     filterChain.doFilter(request, response); 
    } 

} 

và:

@Configuration 
public class RestConfig { 

    @Bean 
    public CORSFilter corsFilter() { 
     CorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 
     CorsConfiguration config = new CorsConfiguration(); 
     config.addAllowedOrigin("http://localhost:4200"); 
     config.addAllowedMethod(HttpMethod.DELETE); 
     config.addAllowedMethod(HttpMethod.GET); 
     config.addAllowedMethod(HttpMethod.OPTIONS); 
     config.addAllowedMethod(HttpMethod.PUT); 
     config.addAllowedMethod(HttpMethod.POST); 
     ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", config); 
     return new CORSFilter(source); 
    } 
} 
0

tôi đã cùng một vấn đề trên một methood trả về tình trạng của máy chủ. Ứng dụng được triển khai trên nhiều máy chủ. Vì vậy, dễ nhất tôi thấy là thêm

@CrossOrigin(origins = "*") 
@RequestMapping(value="/schedulerActive") 
public String isSchedulerActive(){ 
    //code goes here 
} 

Phương pháp này không an toàn nhưng bạn có thể thêm allowCredentials cho điều đó.

Các vấn đề liên quan