likes
comments
collection
share

不再麻烦后端同学的处理跨域问题

作者站长头像
站长
· 阅读数 16

不再麻烦后端同学的处理跨域问题

什么是跨域

每当我们请求后端识别后打开控制台时如果出现了No ‘Access-Control-Allow-Origin’ header is present on the requesting resource 那么这篇文章可能会帮助到你。

跨域是指在网页中,当一个网页试图去访问不同域名下的资源时(比如发送Ajax请求、使用iframe加载其他网页等),会受到同源策略的限制,从而导致无法正常获取数据的情况。

什么是同源策略

同源指的是协议域名端口都相同的两个网址。

例如:如果您要从domain1.com/apidomain1.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的优点是可以实现跨域请求,而且兼容性好,几乎所有浏览器都支持。但是它也有一些弊端:

  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<any> {
    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() 方法将响应数据返回给客户端。

原理如下图所示:

不再麻烦后端同学的处理跨域问题

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会被转发到www.domain2.com

注意,如果在代理配置中设置了changeOrigin为true,则在代理请求时会自动将请求头中的Host字段设置为代理目标的域名,这样就可以绕过浏览器的同源策略,实现跨域请求。

2.在应用中发起请求时,将API请求的路径设置为代理地址即可:

axios.get('/api/data')
  .then(response => {
    console.log(response.data)
  })
  .catch(error => {
    console.log(error)
  })

踩过的坑

值得注意的是:当我们去客户端请求前端自己的服务器时,也是需要设置跨域的,因为请求得端口号不同

结尾

路过的小伙伴觉得有用的话,点点赞收藏下哦❤。