跨域訪問
什麼是跨域
瀏覽器對於javascript的同源策略的限制 。
跨域原因 | 示例 |
域名不同 | aaa.com和bbb.com |
域名相同,埠不同 | aaa.com:81和aaa.com:82 |
二級域名不同 | a.aaa.com和b.aaa.com |
協議不同 | http和https |
為什麼有跨域問題
跨域不一定會有跨域問題。
跨域問題是瀏覽器對於ajax請求的一種安全限制,因此跨域問題是針對ajax的一種限制,一個頁面發起的ajax請求,只能是於當前頁同域名的路徑,這能有效的阻止跨站攻擊。
解決跨域問題的方案
jsonp
最早的解決方案,利用script標籤可以跨域的原理實現,存在一定的侷限性(需要服務的支援,只能發起GET請求)。
nginx反向代理
利用nginx反向代理把跨域為不跨域,支援各種請求方式。
缺點:需要在nginx進行額外配置,語義不清晰
CORS
規範化的跨域請求解決方案,安全可靠。
- 優勢:
- 在服務端進行控制是否允許跨域,可自定義規則。
- 支援各種請求方式。
- 劣勢:
- 會產生額外請求的問題。
jsonp解決跨域問題
jquery的jsonp方式跨域請求
jsonp方式不支援POST方式跨域請求,就算指定成POST方式,會自動轉為GET方式;而後端如果設定成POST方式了,那就請求不了了。
1 $(function(){ 2 var name='cc'; 3 varlooks='handsome'; 4 $.ajax({ 5 url: "http://localhost:9090/student", 6 type: "GET", 7 data:{ 8 name:name, 9 looks:looks 10 }, 11 cache:false, 12 dataType: "jsonp", //指定伺服器返回的資料型別13 jsonp:'theFunction',//用以獲得jsonp回撥函式名的引數名(預設為:callback) 14 15 jsonpCallback: "showData", //指定回撥函式名稱 16 success: function (data) { 17 console.info("呼叫success"); } 18 }); 19 })
基於script標籤實現跨域
凡是擁有src這個屬性的標籤都可以跨域例如<script><img><iframe>。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Insert title here</title> 6 7 <script type="text/javascript"> 8 var message = function(data) { 9 alert(data[1].title); 10 }; 11 </script> 12 13 <script type="text/javascript" src="http://web.cn/js/message.js"></script> 14 </head> 15 <body> 16 <div id='testdiv'></div> 17 </body> 18 </html>
vue.js
1 this.$http.jsonp('http://www.aaa.com:8080/login', { 2 params: {}, 3 jsonp: 'onBack' 4 }).then((res) => { 5 console.log(res); 6 })
Nginx反向代理解決跨域問題
配置Nginx:/nginx-1.18.0/conf/nginx.conf
1 worker_processes 1; 2 3 events { 4 worker_connections 1024; 5 } 6 7 8 http { 9 include mime.types; 10 default_type application/octet-stream; 11 12 sendfile on; 13 keepalive_timeout 65; 14 15 server { 16 listen 80; 17 server_name manage.aaa.com; 18 19 location / { 20 proxy_pass http://127.0.0.1:8001; 21 proxy_connect_timeout 600; 22 proxy_read_timeout 600; 23 } 24 25 location /api { 26 proxy_pass http://127.0.0.1:8002; 27 proxy_connect_timeout 600; 28 proxy_read_timeout 600; 29 } 30 31 error_page 500 502 503 504 /50x.html; 32 location = /50x.html { 33 root html; 34 } 35 } 36 }
CORS解決跨域問題
什麼是CORS
CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。
簡單請求
只要同時滿足以下兩
大條件,就屬於簡單請求。
(1)請求方法是以下三種之一:HEAD、GET、POST。
(2)HTTP的頭資訊不超出以下幾種欄位:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain)。
1 // 白名單(可接受的域),是一個具體域名或者*(代表任意域名),注意:需要攜帶cookie時不能設定為* 2 Access-Control-Allow-Origin: * 3 // 是否允許攜帶cookie,預設情況下,cors不會攜帶cookie,除非這個值是true 4 Access-Control-Allow-Credentials: false 5 Content-Type: text/html; charset=utf-8
要想操作cookie,需要滿足3個條件:
- 服務的響應頭中需要攜帶Access-Control-Allow-Credentials並且為true。
- 瀏覽器發起ajax需要指定withCredentials 為true。
- 響應頭中的Access-Control-Allow-Origin一定不能為*,必須是指定的域名。
特殊請求
不符合簡單請求的條件,會被瀏覽器判定為特殊請求,,例如請求方式為PUT。
預檢請求:特殊請求會在正式通訊之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。
1 OPTIONS /cors HTTP/1.1 2 Origin: http://manage.leyou.com 3 Access-Control-Request-Method: PUT 4 Access-Control-Request-Headers: X-Custom-Header 5 Host: api.leyou.com 6 Accept-Language: en-US 7 Connection: keep-alive 8 User-Agent: Mozilla/5.0...
預檢請求的響應(服務的收到預檢請求,如果許可跨域,會發出響應):
1 HTTP/1.1 200 OK 2 Date: Mon, 01 Dec 2008 01:15:39 GMT 3 Server: Apache/2.0.61 (Unix) 4 Access-Control-Allow-Origin: http://manage.leyou.com 5 Access-Control-Allow-Credentials: true 6 Access-Control-Allow-Methods: GET, POST, PUT 7 Access-Control-Allow-Headers: X-Custom-Header 8 Access-Control-Max-Age: 1728000 9 Content-Type: text/html; charset=utf-8 10 Content-Encoding: gzip 11 Content-Length: 0 12 Keep-Alive: timeout=2, max=100 13 Connection: Keep-Alive 14 Content-Type: text/plain
除了Access-Control-Allow-Origin和Access-Control-Allow-Credentials以外,這裡又額外多出3個頭:Access-Control-Allow-Methods(允許訪問的方式)、Access-Control-Allow-Headers(允許攜帶的頭)、Access-Control-Max-Age(本次許可的有效時長,單位是秒,過期之前的ajax請求就無需再次進行預檢了)。
如果瀏覽器得到上述響應,則認定為可以跨域,後續就跟簡單請求的處理是一樣的了。
java程式碼實現CORS
服務端可以通過攔截器統一實現,不必每次都去進行跨域判定的編寫。
事實上,SpringMVC已經幫我們寫好了CORS的跨域過濾器:CorsFilter ,內部已經實現了剛才所講的判定邏輯,我們直接用就好了。
1 package com.test.config; 2 3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.web.cors.CorsConfiguration; 6 import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 7 import org.springframework.web.filter.CorsFilter; 8 9 /** 10 * @Program: test 11 * @Package: com.test.config 12 * @ClassName: CorsConfiguration 13 * @Description: Cors解決跨域問題 14 * @Author: tankang 15 * @Create: 2020-09-08 23:54 16 */ 17 @Configuration 18 public class CorsConfiguration { 19 @Bean 20 public CorsFilter corsFilter(){ 21 // 初始化cors配置物件 22 CorsConfiguration corsConfiguration = new CorsConfiguration(); 23 corsConfiguration.addAllowedOrigin("http://manage.aaa.com");// 允許跨域的域名,如果需要攜帶cookie,不能寫*,*代表允許所有域名 24 corsConfiguration.setAllowCredentials(true);// 允許攜帶cookie 25 corsConfiguration.addAllowedMethod("*");// *代表所有的請求方法,get/post/put/delete ... 26 corsConfiguration.addAllowedHeader("*");// 允許攜帶任何頭資訊 27 // 初始化cors配置源物件 28 UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); 29 urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); 30 // 返回corsFilter例項,引數cors配置源物件 31 return new CorsFilter(urlBasedCorsConfigurationSource); 32 } 33 }