日期:2019年5月18日html
事情緣由:因爲微信小程序的開發只有測試環境,然後臺提供接口的環境是開發環境;兩個環境的域名不一樣,致使前端開發產生了跨域問題;前端
理論概念:nginx
一、同源策略:同源策略是瀏覽器的安全基石,存儲在瀏覽器中的數據(cookie)必須在同域(相同域名)下才能任意的讀取,而非同域下的瀏覽器資源是不容許任意操做的。即同源策略下,非同源的網站之間不能發送Ajax請求;若是沒有同源策略,會致使瀏覽器中的數據能夠被任何來源請求進行訪問與資源操做,存儲在瀏覽器中的數據就沒有任何安全可言;git
二、域名:相同域名(www.baidu .com;全部的請求都來自於這個域名下,即爲同域下);github
不一樣域名(www.baidu.com,www.google.com;來自google的請求不能直接操做baidu域下的資源);web
三、同源:兩個頁面的【協議】、【端口(若是指定了)】、【主機】都相同,則這兩個頁面具備相同的源。有一個元素不一樣,則是不一樣源的;spring
四、跨域資源共享(CORS):sql
1)它是一份瀏覽器技術的規範,提供Web服務從不一樣域傳來沙盒腳本的方法,泳衣避開瀏覽器的同源策略,JSONP模式的現代版(相關JSONP的知識請本身查閱,本文再也不作解釋說明);小程序
2)實現思路是使用自定義的HTTP頭部制定一個能夠互相承認的約定(例如瀏覽器給服務器傳送一個頭信息A,服務器給瀏覽器傳送一個頭信息B,就能夠不受同源策略的限制),從而決定請求與響應成功與否;後端
3)CORS不關心安全問題,只解決資源共享的問題;安全性能夠從請求時效性,token驗證,IP驗證,來源驗證等方面考慮安全問題;
五、CORS與JSONP的區別:
1)JSONP只能實現GET請求,而CORS支持全部的請求;
2)使用CORS,前端開發者可使用普通的XMLHttpRequest發起的請求和響應的數據,比JSONP有更好的錯誤處理;
3)JSONP主要被版本比較老的瀏覽器支持,這些老版本的瀏覽器每每不支持CORS(IE6,IE7,Open mini),而絕大多數現代瀏覽器都已經支持了CORS;
六、注:在跨域請求中,請求是能夠發送到服務器的,服務器也能夠響應數據,但服務器響應到瀏覽器的數據,瀏覽器拒絕解析(不太肯定是拒絕接受仍是拒絕解析?),致使的資源沒法共享;
解決方案:
只要解決跨域資源共享便可,CROS解決方案主要有如下幾種:
一、自定義CORSFilter(Intercepter):適用於設置單一的(所有)受權訪問,全部配置固定,操做簡單,不根據請求類型作不一樣的處理,粒度比較大;
1 @Bean 2 public FilterRegistrationBean corsFilter() { 3 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 4 CorsConfiguration config = new CorsConfiguration(); 5 config.setAllowCredentials(true); config.addAllowedOrigin("http://localhost:9000"); 6 config.addAllowedOrigin("null"); 7 config.addAllowedHeader("*"); 8 config.addAllowedMethod("*"); 9 source.registerCorsConfiguration("/**", config); // CORS 配置對全部接口都有效 10 FilterRegistrationBean bean = newFilterRegistrationBean(new CorsFilter(source)); 11 bean.setOrder(0); 12 return bean; 13 }
二、Nginx代理配置(配置在location中)
1 # 2 # Wide-open CORS config for nginx 3 # 4 location / { 5 if ($request_method = 'OPTIONS') { 6 add_header 'Access-Control-Allow-Origin' '*'; 7 add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 8 # 9 # Custom headers and headers various browsers *should* be OK with but aren't 10 # 11 add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 12 # 13 # Tell client that this pre-flight info is valid for 20 days 14 # 15 add_header 'Access-Control-Max-Age' 1728000; 16 add_header 'Content-Type' 'text/plain charset=UTF-8'; 17 add_header 'Content-Length' 0; 18 return 204; 19 } 20 if ($request_method = 'POST') { 21 add_header 'Access-Control-Allow-Origin' '*'; 22 add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 23 add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 24 } 25 if ($request_method = 'GET') { 26 add_header 'Access-Control-Allow-Origin' '*'; 27 add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 28 add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 29 } 30 }
1 #js跨域支持 2 #add_header 'Access-Control-Allow-Origin' '*'; 3 #add_header 'Access-Control-Allow-Credentials' 'true'; 4 #add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With'; 5 #add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
三、使用spring框架提供的註解@CrossOrigin
1)第一種狀況,對接口的粒度進行配置
1 @CrossOrigin(origins = {"http://localhost:9000", "null"}) 2 @RequestMapping(value = "/test", method = RequestMethod.GET) 3 public String greetings() { 4 return "{\"project\":\"just a test\"}"; 5 }
2)第二種狀況,對類的粒度進行配置
1 @CrossOrigin(origins = {"http://localhost:9000", "null"}) 2 @RestController 3 @SpringBootApplication 4 public class SpringBootCorsTestApplication { 5 // xxx 6 }
四、全局配置
1 @Configuration 2 public class WebConfig extends WebMvcConfigurerAdapter { 3 4 @Override 5 public void addCorsMappings(CorsRegistry registry) { 6 registry.addMapping("/**") 7 .allowedOrigins("http://localhost:9000", "null") 8 .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") 9 .maxAge(3600) 10 .allowCredentials(true); 11 } 12 }
五、支持多域名配置的CORSFilter
由於知道已經有能夠用的庫能夠解決,因此就沒重複造輪子了。其實由於懶,看看別人的源碼算了.在mvnrepository搜索cors-filter,目前也就兩個能夠用
org.ebaysf.web 的 cors-filter,項目地址:https://github.com/ebay/cors-filter
com.thetransactioncompany的 cors-filter,項目地址:http://software.dzhuvinov.com/cors-filter.htm
-------------------------------------------------------------------------------------------------
Ps:與前端的交互:
非簡單請求的跨源請求,瀏覽器會在真實請求發出前,增長一次 OPTION 請求,稱爲預檢請求( preflight request )。預檢請求將真實請求的信息,包括請求方法、自定義頭字段、源信息添加到 HTTP 頭信息字段中,詢問服務器是否容許這樣的操做。
好比對於 DELETE
請求:
OPTIONS /test HTTP/1.1 Origin: http://www.examples.com Access-Control-Request-Method: DELETE Access-Control-Request-Headers: X-Custom-Header Host: www.examples.com
與 CORS 相關的字段有:
Access-Control-Request-Method
: 真實請求使用的 HTTP 方法;
Access-Control-Request-Headers
: 真實請求中包含的自定義頭字段。
服務器收到請求時,須要分別對 Origin 、 Access-Control-Request-Method 、 Access-Control-Request-Headers 進行驗證,驗證經過後,會在返回 Http 頭信息中添加
Access-Control-Allow-Origin: http://www.examples.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: X-Custom-Header Access-Control-Allow-Credentials: true Access-Control-Max-Age: 1728000
他們的含義分別是:
當預檢請求經過後,瀏覽器會發送真實請求到服務器。這就實現了跨源請求
-------------------------------------------------------------------------------------------------
總結:以上提供的幾種方案均可以解決跨域問題,視本身業務的實際狀況選擇方案;我的選擇的是最簡單的Nginx,不對代碼進行任何的修改,直接配置在代理中;我的以爲要達到比較細粒度的各類優化,推薦使用第五種方案,但同時會會增長學習成本,對時間比較充足的同窗能夠研究研究;
雖然跨域調用的問題解決了,可是後來發現以上方法並不能解決cookies的跨域問題,致使在A域名下點擊登陸後直接跳轉到B域名下已登錄後的一個頁面,會失敗。主要問題是在A域名下獲取到的cookie不能共享到B域名下,致使跳轉B域名下已登陸的頁面會被斷定沒有coolie(沒有登陸),會致使直接跳轉到B域名下的登陸頁面進行從新登陸,即在A域名下的登陸失敗了;思考了幾種解決方案:①後端修改登陸接口的代碼邏輯,適配出現的這種狀況(建立新的cookie,或者由後端進行路由),②在A域名下點擊登陸時,直接跳轉到B域名下的一個和A域名頁面同樣的登陸頁面,不給用戶感知,但域名會發生變化,在B域名下的登陸頁面進行實際的登陸操做,就不會再產生cookie跨域的問題了,③直接使用nginx進行路由:
以上三種解決思路可供參考,我本身選用的第二種,若是後端和運維支持比較給力,推薦選用第一種或者第三種解決方案!!!!