不再麻煩後端同學的處理跨域問題

語言: CN / TW / HK

theme: nico highlight: atom-one-dark


不再麻煩後端同學的處理跨域問題

什麼是跨域

每當我們請求後端識別後打開控制枱時如果出現了No ‘Access-Control-Allow-Origin’ header is present on the requesting resource 那麼這篇文章可能會幫助到你。

跨域是指在網頁中,當一個網頁試圖去訪問不同域名下的資源時(比如發送Ajax請求、使用iframe加載其他網頁等),會受到同源策略的限制,從而導致無法正常獲取數據的情況。

什麼是同源策略

同源指的是協議域名端口都相同的兩個網址。

例如:如果您要從http://domain1.com/api 向http://domain1.com 提出請求,則該請求將通過。

image.png

如果提出向另一個域的請求,www.domain2.com/api ,瀏覽器將阻止請求。

image.png

為什麼瀏覽器需要有跨域

瀏覽器之所以限制跨域訪問,是出於安全考慮。

如果瀏覽器不限制跨域訪問,那麼攻擊者就可以偽造請求,訪問用户在其他網站上的敏感信息或執行惡意操作。

舉一個簡單的例子:

假設受害者在網站A上登錄了自己的賬户,攻擊者在網站B上發佈了一個誘騙用户點擊的鏈接,鏈接指向一個惡意網站C,該網站C使用JavaScript代碼向網站A發起了一個跨域請求,偷取了受害者在網站A上的敏感信息(例如cookie、用户名、密碼等)。

攻擊者可以將以下代碼嵌入到網站C中,使用XMLHttpRequest對象向網站A發起跨域請求: ``` var xhr = new XMLHttpRequest(); xhr.open("GET", "http://www.domain2.com/user-info", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { alert(xhr.responseText); // 將獲取的敏感信息上傳到攻擊者的服務器上 // ... } } xhr.send();

``` 這段代碼在網站C中被執行,向網站A發起了一個跨域請求,偷取了受害者在網站A上的敏感信息。攻擊者可以將獲取的信息上傳到自己的服務器上,用於非法用途,例如冒充受害者登錄網站A、竊取賬户資金等。

所以跨域訪問如果可以打破同源策略,會給個人隱私和財產安全帶來極大的威脅。為了保護用户的安全,瀏覽器限制了跨域訪問。

如何優雅的處理

每當我們遇到這個問題時,最快的解決方法就是喊後端加一下enable cors,那有沒有前端可以自己想辦法解決的呢?

有以下三種常用的方式: 1. JSONP 2. 代理的方式 3. Webpack devserver

JSONP

大白話,JSONP是一種利用script標籤的GET請求實現跨域的技術。 JSONP一個簡單的案例:

假設我們有一個網站A,想要獲取另一個網站B的數據,但是B和A不在同一個域,因此不能直接訪問。

B網站的服務器端,提供了一個名為getData的接口,可以返回一些數據。為了讓A網站可以獲取這些數據,B網站在返回數據時將其封裝在一個名為callback的回調函數中,並將回調函數的參數作為需要的數據傳給A網的。A網站使用JSONP來獲取B網站的數據,並指定一個名為handleResponse的回調函數。

B網站後端返回數據的代碼如下: ``` var data = { "name": "Alice", "age": 18 }; var callback = req.query.callback; // 獲取回調函數的名稱 res.send(callback + '(' + JSON.stringify(data) + ')'); // 將數據封裝在回調函數中返回

A網站前端請求代碼如下: function handleResponse(data) { console.log(data); }

var script = document.createElement('script'); script.src = 'http://www.domain2.com/getData?callback=handleResponse'; document.head.appendChild(script);

``` JSONP的優點是可以實現跨域請求,而且兼容性好,幾乎所有瀏覽器都支持。但是它也有一些弊端:

  1. 只支持GET請求。因為JSONP是通過動態創建script標籤來實現跨域請求的,而script標籤只支持GET請求。
  2. 數據格式限制。JSONP只能返回JSON格式的數據,無法返回XML格式的數據。

代理的方式

大白話,就是自己搭一個本地服務器,通過訪問服務器與服務器的關係,跨過瀏覽器這的同源策略

我以Nest.js自己搭建一個簡單的代理:

``` import { Controller, Post, Req, Res, Body } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { Request, Response } from 'express'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Controller() export class AppController { constructor(private readonly httpService: HttpService) {}

@Post('/api') proxy( @Req() request: Request, @Res() response: Response,

@Body() data: any,

): Observable { const url = request.query.url as string; const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; return this.httpService.post(url, data, { headers }).pipe( map((res) => { response.send(res.data); }), ); } } ```

在上述代碼中,我們通過@Body() 裝飾器來獲取請求的數據,並將其作為參數傳遞給 this.httpService.post() 方法。同時,使用 request.query 來獲取請求的地址。在請求成功時,通過 response.send()方法將響應數據返回給客户端。

原理如下圖所示:

image.png

Webpack devserver

如果是大型項目,並且配置了webpack的話。

1.可以在Webpack的配置文件中添加devServer.proxy屬性來實現跨域代理。具體實現方式如下: ``` // webpack.config.js

module.exports = { // ... devServer: { proxy: { '/api': { target: 'http://www.domain2.com', //需要跨域的url changeOrigin: true, pathRewrite: { '^/api': '' } } } } }

```

在上面的代碼中,/api會被轉發到http://www.domain2.com

注意,如果在代理配置中設置了changeOrigin為true,則在代理請求時會自動將請求頭中的Host字段設置為代理目標的域名,這樣就可以繞過瀏覽器的同源策略,實現跨域請求。

2.在應用中發起請求時,將API請求的路徑設置為代理地址即可: ``` axios.get('/api/data') .then(response => { console.log(response.data) }) .catch(error => { console.log(error) })

```

踩過的坑

值得注意的是:當我們去客户端請求前端自己的服務器時,也是需要設置跨域的,因為請求得端口號不同

結尾

路過的小夥伴覺得有用的話,點點贊收藏下哦❤。