Vue跨域

丿灬透?發表於2020-09-30

1、什麼是跨域

跨域指瀏覽器不允許當前頁面的所在的源去請求另一個源的資料。源指協議,埠,域名。只要這個3箇中有一個不同就是跨域。 這裡列舉一個經典的列子:

#協議跨域
http://a.baidu.com訪問https://a.baidu.com;
#埠跨域
http://a.baidu.com:8080訪問http://a.baidu.com:80;
#域名跨域
http://a.baidu.com訪問http://b.baidu.com;
  現在很多公司都是採用前後分離的方式開發。那麼出現經常和會跨域打交道。我這裡整理日常開發中解決跨域的幾種方案。我們前端使用的Vue,後端使用的NodeJs。
  在這裡插入圖片描述

1.1、 什麼是同源策略?

同源策略/SOP(Same origin policy)是一種約定,由 Netscape 公司 1995 年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能,現在所有支援 JavaScript 的瀏覽器都會使用這個策略。如果缺少了同源策略,瀏覽器很容易受到 XSS、CSFR 等攻擊。所謂同源是指"協議+域名+埠"三者相同,即便兩個不同的域名指向同一個 ip 地址,也非同源。

1.2、 非同源策略限制以下幾種行為:

1.) Cookie、LocalStorage 和 IndexDB 無法讀取

2.) DOM 和 Js 物件無法獲得

3.) AJAX 請求不能傳送

1.3、 常見跨域場景

在這裡插入圖片描述

2、跨域解決方案

1) 通過 jsonp 跨域

2) document.domain + iframe 跨域

3) location.hash + iframe

4) window.name + iframe 跨域

5) postMessage 跨域

6) 跨域資源共享(CORS)

7) nginx 代理跨域

8) nodejs 中介軟體代理跨域

9) WebSocket 協議跨域

2.1、proxyTable

這裡vue腳手架生成的標準專案為準。一般在專案config目錄下面有個index檔案。裡面格式如下:

'use strict'
const path = require('path')
module.exports = {
    dev: {
        // Paths
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        proxyTable: {
            '/api': {
                target: 'http://localhost:7001',//後端介面地址
                changeOrigin: true,//是否允許跨越
                pathRewrite: {
                    '^/api': '/api',//重寫,
                }
            }
        },
        host: '192.168.0.104',
        port: 8081,
        autoOpenBrowser: false,
        errorOverlay: true,
        notifyOnErrors: true,
        poll: false,
        useEslint: true,
        showEslintErrorsInOverlay: false,
        devtool: 'eval-source-map',
        cacheBusting: true,
        cssSourceMap: false,
    },

}

上面配置中,我們根據實際情況只需要修改proxyTable對於配置即可。假設我後端請求地址是http://localhost:7001,所有api的介面url都以/api開頭。所以首先需要匹配所有以/api開頭的.然後修改target的地址為http://localhost:7001。最後修改pathRewrite地址。將字首 ‘^api’ 轉為 ‘/api’。如果本身的介面地址就有 ‘/api’ 這種通用字首,就可以把 pathRewrite 刪掉。注意這個方式只能在開發環境中使用。
JsonP 優缺點

JSONP 的優點是:它不像 XMLHttpRequest 物件實現的 Ajax 請求那樣受到同源策略的限制;它的相容性更好,在更加古老的瀏覽器中都 可以執行,不需要 XMLHttpRequest 或 ActiveX 的支援;並且在請求完畢後可以通過呼叫 callback 的方式回傳結果。

JSONP 的缺點則是:它只支援 GET 請求而不支援 POST 等其它型別的 HTTP 請求;它只支援跨域 HTTP 請求這種情況,不能解決不同域的兩個頁面之間如何進行 JavaScript 呼叫的問題。

2.2、 JsonP

JSONP 的優點是:它不像 XMLHttpRequest 物件實現的 Ajax 請求那樣受到同源策略的限制;它的相容性更好,在更加古老的瀏覽器中都 可以執行,不需要 XMLHttpRequest 或 ActiveX 的支援;並且在請求完畢後可以通過呼叫 callback 的方式回傳結果。

JSONP 的缺點則是:它只支援 GET 請求而不支援 POST 等其它型別的 HTTP 請求;它只支援跨域 HTTP
請求這種情況,不能解決不同域的兩個頁面之間如何進行 JavaScript 呼叫的問題。

搭建跨域場景

需求:

1)建立兩個 web 工程,名稱為 jsonDemo1(8080)、jsonDemo2(9090)

2)jsonDemo1 中提供一個 index.jsp。

3)在 jsonDemo1 的 index.jsp 中通過 Jquery 的 Ajax 跨域請求 jsonDemo2

4)jsonDemo2 中使用 springMVC 處理請求,返回一個 json 物件

5)在 jsonDemo1 中將返回的結果插入到 index.jsp 中
建立專案

jsonDemo1
在這裡插入圖片描述
jsonDemo2
在這裡插入圖片描述
使用 JsonP 解決跨域:

Ajax 跨域請求時會出現異常、
在這裡插入圖片描述
在 ajax 中請求方式有所改變

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2019/10/11
  Time: 9:18
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首頁</title>
    <script type="text/javascript" src="/js/jquery-1.7.2.js"></script>
    <script type="text/javascript">
        $(function () {

            $("#but").click(function () {
                $.ajax({
                    type:"get",
                    url:"http://localhost:9090/user/findUser",
                    dataType:"jsonp",
                    jsonp:"callback",
                    success:function (data) {
                        alert(data);
                        var str="";
                        for (i=0;i<data.length;i++){
                            str+=data[i].userid+" "+data[i].username+" "+data[i].userage;
                        }
                        $("#sp").html(str);
                    }
                })

            })
        })
    </script>
</head>
<body>
<h1>歡迎</h1>
<span id="sp"></span>
<input type="button" value="ok" id="but">
</body>
</html>
 

請求的 Controller 需要改變(SpringMVC 對 JsonP 的支援)

package com.bjsxt.web.controller;

import com.bjsxt.comments.JsonUtils;
import com.bjsxt.pojo.Users;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/findUser")
    @ResponseBody
    public Object findUser(String callback){
        Users users1=new Users(1,"admin",20);
        Users users2=new Users(2,"zhangsan",20);
        Users users3=new Users(3,"lisi",20);
        List<Users> list= new ArrayList<>();
        list.add(users1);
        list.add(users2);
        list.add(users3);
        //json轉換
        MappingJacksonValue mv=new MappingJacksonValue(list);
        mv.setJsonpFunction(callback);
        /*String json=JsonUtils.objectToJson(list);*/
        return mv;


    }
}

2.3、 CORS

CORS即跨源資源共享,它定義了一種瀏覽器和伺服器互動的方式來確定是否允許跨域請求。它是一個妥協,有更大的靈活性,但比起簡單地允許所有這些的要求來說更加安全。但是CORS也具有一定的風險性,比如請求中只能說明來自於一個特定的域但不能驗證是否可信,而且也容易被第三方入侵。 這裡一般需要後端配合,開啟cors。一般各種語言都有類似的包。比如NodeJS的koa2-cors

var koa = require('koa');
//npm install --save koa2-cors
var cors = require('koa2-cors');
var app = koa();
//開啟
app.use(cors());

這個方式解決的跨越問題支援開發和生產環境。但是有一定的安全性問題。

2.4、 Nginx

當我們明白跨越的含義之後。只要解決了’源’的問題。那麼跨越也就不存在了。這裡我們便會想到proxy,同時也會想到Nginx。

相關文章