CORS 是一個 W3C 標準,全稱是」跨域資源共享」(Cross-origin resource sharing)。它容許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了 AJAX 只能同源使用的限制(跨域資源共享 CORS 詳解)。html
解決 CORS 跨域方法大體有以下幾類:前端
Spring Boot 實現 CORS 跨域 (官方)vue
@CrossOrigin
的註解實現可是若是項目中包含 Spring Security 就會有 401 的問題,Spring Security 自己是經過 Filter 實現的,若是沒有對其單獨作 CORS 的處理,在 Web Security 報錯 401 的時候是不會返回相應的 CORS 的字段的。這會致使出現的 401 錯誤成爲了一個沒法進行跨域的錯誤,致使前端程序沒法正常的處理 401 相應 。對於spring security oauth2 默認接口,例如 /oauth/token 跨域問題,能夠經過全局 CORS Filter 解決。git
@Configuration public class GlobalCorsConfiguration { @Bean public CorsFilter corsFilter() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowCredentials(true); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); // corsConfiguration.addExposedHeader("head1"); //corsConfiguration.addExposedHeader("Location"); UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); return new CorsFilter(urlBasedCorsConfigurationSource); } }
配置 CorsFilter 優先級與 Spring Security 不攔截 OPTIONS 請求github
配置服務器容許 /oauth/token的 OPTIONS 方法,由於 /oauth/token 接口是先發一個 OPTIONS 請求,而後再發送 POST請求,若是是 OPTIONS 接口不被容許,就會返回 401 錯誤。spring
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) @Order(-1) public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // http.requestMatchers().antMatchers(HttpMethod.OPTIONS, "/oauth/**","/login/**","/logout/**") // .and() // .authorizeRequests() // .antMatchers().permitAll() // .and() // .formLogin().permitAll(); //新增login form 支持用戶登陸及受權 http.requestMatchers().antMatchers(HttpMethod.OPTIONS, "/oauth/**") .and() .cors() .and() .csrf().disable(); } }
再次 Vue(vue-resource) 前端測試調用,問題解決。json
this.$http.post('http://47.100.188.242:8888/oauth/token', {'grant_type':'password','scope':'read','username':'test','password':'test'} ,{emulateJSON:true,headers:{Authorization: 'Basic Y2ssxpZW50Xzkd5c3VsuX3dlYjpzwZWNyZXRfOTlezdsW5fMTIzNDU2'}}).then(function(){ }, function(){ });
報文api
OPTIONS http://localhost:8888/oauth/token HTTP/1.1 Host: localhost:8888 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Access-Control-Request-Method: POST Origin: http://localhost:5000 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36 Access-Control-Request-Headers: authorization Accept: */* Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 HTTP/1.1 200 Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Access-Control-Allow-Origin: http://localhost:5000 Access-Control-Allow-Methods: POST Access-Control-Allow-Headers: authorization Access-Control-Allow-Credentials: true 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 Content-Length: 0 Date: Thu, 27 Sep 2018 04:38:56 GMT ------------------------------------------------------------------- POST http://localhost:8888/oauth/token HTTP/1.1 Host: localhost:8888 Connection: keep-alive Content-Length: 60 Pragma: no-cache Cache-Control: no-cache Accept: */* Origin: http://localhost:5000 Authorization: Basic Y2xpZW50Xzks5c3VuX3dlYjpzZWNyZXRfOsTlzdW5fMTIzNDUs2 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36 Content-Type: application/x-www-form-urlencoded Referer: http://localhost:5000/test.html Accept-Encoding: gzip, deflate, br grant_type=password&username=test&password=test&soap=api HTTP/1.1 200 Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Access-Control-Allow-Origin: http://localhost:5000 Access-Control-Allow-Credentials: true Cache-Control: no-store Pragma: no-cache X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block X-Frame-Options: DENY Content-Type: application/json;charset=UTF-8 Date: Thu, 27 Sep 2018 04:38:56 GMT Content-Length: 1560 {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJpcnZpbmciLCJzY29wZSI6WyJhbGwiLCJhcGkiLCJ1c2VyIiwicmVhZCIsIndyaXRlIl0sImV4dF9uYW1lIjoiaXJ2aW5nIiwiZXhwIjoxNTM4MDMwMjk4LCJjbGllbnRfbmFtZSI6ImlydmluZyIsImp0aSI6IjIwOTg1ZjJhLWNiMGUtNDRiZi1hZWIyLTYzMGQ5NWFhNDI3ZSIsImNsaWVudF9pZCI6ImNsaWVudF85OXN1bl93ZWIifQ.koBwYKeLGr0KgZkOAN9ENGtKtHyQnzBRR_b0N1Ck1g2hk5VOGKikBTcCf-HBycHDvFZzADyqzi1640JDWo7MwwZm72r4Ih_QN-A_CztAMLyxsMvotMx9Du4z8wOJ4qaOGEuFGj3HFdFaMG3ltaN1WnoxORolFLqd6O-Q21SUhXaOMldOUZ_AyQildNnJ3-EJSavVSEc78jnW0P5fEJtp7QpRTS6nyvwwQifD7uoshnPWWbAeX7rYfAhEie3m7csx6sB_7nnJavKyk_AUpddioOgvw8QDZSmIGPKDeLlFLNpq1NGssgCqECyxdwmWVCs3x7rr6CwRDFfva87moJQAIQ","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJpcnZpbmciLCJzY29wZSI6WyJhbGwiLCJhcGkiLCJ1c2VyIiwicmVhZCIsIndyaXRlIl0sImF0aSI6IjIwOTg1ZjJhLWNiMGUtNDRiZi1hZWIyLTYzMGQ5NWFhNDI3ZSIsImV4dF9uYW1lIjoiaXJ2aW5nIiwiZXhwIjoxNTM4MDIzMjc1LCJjbGllbnRfbmFtZSI6ImlydmluZyIsImp0aSI6IjRiNTc1MTVlLWEwOTUtNGZiMS05MGQ3LTE0MjQyOTcyMWI1YyIsImNsaWVudF9pZCI6ImNsaWVudF85OXN1bl93ZWIifQ.E8gviZsAb8ci_PMAzj4Oj7bqopr8xNwG3LAwa-pi987yVhg7CTlDhD0QOZLqHVPViwMY7dql-j2ccefwpZsfeaL4i1x5xouANoJ-zRJi7aruxJ_3guy2Ln0fReEYnOnkzKRGjWkdeCbxmrFgg0TkDASB_vTsegsbjqpVfCRg7LAvIcZwQCj1uiSqV8jaHbadvZpA1yZXt7lOMILHwtKkcEOkM7xDUbCHrG1J6qNjRNRbZhI34xXzP0EdXq8_FBA-ykpI1NWTt8jqYtQJfjyZCb9StcQmTqIq234d1ES6uiyZz-pqaiAo7qiVbrK85uOPmuCzuYtZcbWs8neiDeL8_g","expires_in":7161,"scope":"all api user read write","client_name":"irving","ext_name":"irving","jti":"20985f2a-cb0e-44bf-aeb2-630d95aa427e"}
備註:跨域
Spring Security 文檔中 (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors)定義的方式。 瀏覽器
@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; } }
Spring Cloud Gateway (http://cloud.spring.io/spring-cloud-gateway/single/spring-cloud-gateway.html#_cors_configuration)
spring: cloud: gateway: globalcors: corsConfigurations: '[/**]': allowedOrigins: "docs.spring.io" allowedMethods: - GET
ZUUL
https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.1.0.M3/single/spring-cloud-netflix.html#_enabling_cross_origin_requests
@Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/path-1/**") .allowedOrigins("http://allowed-origin.com") .allowedMethods("GET", "POST"); } }; }
REFER:
https://aisensiy.github.io/2017/11/08/spring-cors-and-security/
http://www.spring4all.com/article/177
https://blog.csdn.net/GeorgeShaw1/article/details/75089734
https://stackoverflow.com/questions/37516755/spring-boot-rest-service-options-401-on-oauth-token
https://github.com/pagekit/vue-resource
http://cloud.spring.io/spring-cloud-gateway/single/spring-cloud-gateway.html#_cors_configuration