spring-boot + jsonp 解決前端跨域問題

擁抱心中的夢想發表於2018-06-12

現在我們們一起來討論瀏覽器跨域請求資料的相關問題。說這樣可能不是很標準,因為拒絕跨域請求資料並不是瀏覽器所獨有的,之所以會出現跨域請求不了資料,是因為瀏覽器基本都實現了一個叫"同源策略"的安全規範。該規範具體是什麼呢?我們在MDN上找到了一份資料,地址如下:

瀏覽器同源策略講解

總的來說,當A網址和B網址在協議域名方面存在不同時,瀏覽器就會啟動同源策略,拒絕A、B伺服器之間進行資料請求。

說了同源策略,紙上得來終覺淺,絕知此事要躬行,到底同源策略是怎麼體現的呢?下面我將結合程式碼一步一步進行演示。

1、A伺服器請求不了B伺服器的情況

既然是跨域,我就假設我有兩個域名,分別是Alocalhost,A表示小編在阿里雲上主機域名,localhost顧名思義就是小編的開發機器了。我們想象這樣一個場景,在localhost上部署一個index.html檔案,在A伺服器上部署一個簡單的spring-boot後臺服務,並提供一個簡單的介面暴露給index.html檔案呼叫,最後瀏覽器請求localhostindex.html檔案,看瀏覽器提示什麼?

index.html

<!DOCTYPE html>
<html>
<head>
<title>測試跨域訪問</title>
<meta charset="utf-8"/>
</head>
<body>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function() {
            $.ajax({
                type : "get",
                async : true,
                url : "http://A/hello/map/getUser.json",// 請求A伺服器上的介面
                type : "json",
                success : function(data) {
                // 列印返回的資料
                console.log("success,and return data is " + data);
                }
            });
        });
    </script>
        <h2>hello world</h2>
</body>
</html>
複製程式碼

瀏覽器上請求index.html檔案,顯示如下:

spring-boot + jsonp 解決前端跨域問題

可以發現,請求被瀏覽器給拒絕了,提示我們不允許跨域請求資料,很難受,怎麼解決呢?

2、使用jsonp解決跨域請求

首先講下原理,jsonp解決跨域問題主要利用了<script>標籤的可跨域性,也就是src屬性中的連結地址可以跨域訪問的特性,因為我們經常將src屬性值設定為cdn的地址,已載入相關的js庫。

index.html

<!DOCTYPE html>
<html>
<head>
<title>測試跨域訪問</title>
<meta charset="utf-8" />
</head>
<body>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script type="text/javascript">
    	$(document).ready(function() {
        	$.ajax({
        		type : "get",
        		async : true,
        		jsonp : "callbackName",// 後端介面引數名
        		jsonpCallback : "callbackFunction", // 回撥函式名
        		url : "http://A/hello/map/getUser.json",
        		dataType : "jsonp", // 資料格式為 jsonp
        		success : function(data) {
        			console.log("success");
        		}
        	});
    	});
    </script>
    <script type="text/javascript">
    	var callbackFunction = function(data) {
    		alert('介面返回的資料是:' + JSON.stringify(data));
    	};
    </script>
</body>
</html>
複製程式碼

A伺服器上的介面程式碼為:

/**
 * 
 * The class JsonBackController.
 *
 * Description:該控制器返回一串簡單的json資料,json資料由一個簡單的User物件組成
 *
 * @author: huangjiawei
 * @since: 2018年6月12日
 * @version: $Revision$ $Date$ $LastChangedBy$
 *
 */
@RestController
@RequestMapping(value = "/map")
public class JsonBackController {
    private static final Logger logger = LoggerFactory.getLogger(JsonBackController.class);
    /**
     * 解決跨域請求資料
     * @param response
     * @param callbackName 前端回撥函式名
     * @return
     */
    @RequestMapping(value = "getUser.json")
    public void getUser(HttpServletResponse response, @RequestParam String callbackName) {
        User user = new User("huangjiawei", 22);
        response.setContentType("text/javascript");
        Writer writer = null;
        try {
        	writer = response.getWriter();
        	writer.write(callbackName + "(");
        	writer.write(user.toString());
        	writer.write(");");
        } catch (IOException e) {
        	logger.error("jsonp響應寫入失敗! 資料:" + user.toString(), e);
        } finally {
        	if (writer != null) {
        		try {
        			writer.close();
        		} catch (IOException e) {
        			logger.error("輸出流關閉異常!", e);
        		}
        		writer = null;
        	}
        }
    }
}
複製程式碼

後端傳入一個引數callbackName回撥函式名,然後返回一段js程式碼給前端,js程式碼格式如下:

callbackName + ( data ) + ;

瀏覽器請求localhost伺服器上的index.html檔案,結果如下:

spring-boot + jsonp 解決前端跨域問題

上面這種方式是通過jquery + jsonp解決跨域問題的,剛剛不是說可以用<script>標籤的src屬性嗎?四的。

localhost伺服器上的index.html

<!DOCTYPE html>
<html>
<head>
<title>測試跨域訪問</title>
<meta charset="utf-8" />
</head>
<body>
	<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
	<script type="text/javascript">
		var callbackFunction = function(data) {
			alert('介面返回的資料是:' + JSON.stringify(data));
		};
	</script>
	<script type="text/javascript" src="http://A/hello/map/getUser.json?callbackName=callbackFunction"></script>
</body>
</html>
複製程式碼

瀏覽器顯示效果和上面一致。但此處需要注意的是,src表示引入一個js檔案,由於是直接呼叫介面,而介面返回的資料又剛好是一段js程式碼,故能被執行。另外,第二個<script>標籤順序不能顛倒,不然會出現callbackFunction函式找不到的情況。

工程程式碼地址 : github.com/SmallerCode…

最後總結下,解決跨域的方案有很多種,jsonp只是其中一種,具體情況需要具體分析。希望此文對你有幫助,謝謝閱讀,歡迎github給顆start,麼麼噠!

相關文章