不再麻煩後端同學的處理跨域問題
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 提出請求,則該請求將通過。
如果提出向另一個域的請求,www.domain2.com/api ,瀏覽器將阻止請求。
為什麼瀏覽器需要有跨域
瀏覽器之所以限制跨域訪問,是出於安全考慮。
如果瀏覽器不限制跨域訪問,那麼攻擊者就可以偽造請求,訪問用户在其他網站上的敏感信息或執行惡意操作。
舉一個簡單的例子:
假設受害者
在網站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的優點是可以實現跨域請求,而且兼容性好,幾乎所有瀏覽器都支持。但是它也有一些弊端:
- 只支持GET請求。因為JSONP是通過動態創建script標籤來實現跨域請求的,而script標籤只支持GET請求。
- 數據格式限制。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
在上述代碼中,我們通過@Body()
裝飾器來獲取請求的數據,並將其作為參數傳遞給 this.httpService.post()
方法。同時,使用 request.query 來獲取請求的地址。在請求成功時,通過 response.send()
方法將響應數據返回給客户端。
原理如下圖所示:
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) })
```
踩過的坑
值得注意的是:當我們去客户端請求前端自己的服務器時,也是需要設置跨域的,因為請求得端口號不同
。
結尾
路過的小夥伴覺得有用的話,點點贊收藏下哦❤。