星战时空·网游活动中心

Ajax跨域解决

1.何为跨域

AJAX跨域问题是指浏览器出于安全考虑,限制了一个源(域名、协议、端口)的脚本与另一个源的资源进行交互。通俗的说,当前页面的域名、协议、端口必须与ajax访问地址一致,才能正常通信,否则会造成跨域

2.解决方案

浏览器开放限制:浏览器下载插件(Access-Control-Allow-Origin)

代理服务器:在服务器端(前端所在服务器)设置一个代理服务器,所有AJAX跨域请求都发送到这个代理服务器,由代理服务器转发请求到目标服务器,并返回响应,相当于做了一个中转

CORS:服务器端(接口所在服务器)设置Access-Control-Allow-Origin响应头,允许特定的或所有域进行跨域请求

JSONP:通过 script 标签的src属性请求一个带参数的服务器端脚本,服务器端脚本输出一个指定函数的调用,该函数的参数是要传递的数据

3.Access-Control-Allow-Origin

在chrome应用商店中搜 "cors",找到 "Access-Control-Allow-Origin"

点击添加,安装完成后,启用该插件

4.CORS

http-server:启动时添加 --cors 参数,所有访问这个服务器的请求都允许跨域

http-server -c-1 -p 8097 --cors

express设置允许跨域

//引入express

const express = require("express")

//创建服务对象

let app = express()

app.all('*', function (req, res, next) {

//跨域允许访问的域名(通配符表示所有域名皆可访问,多个域名用都好隔开)

res.header("Access-Control-Allow-Origin",'*')

//跨域允许的header包含哪些字段

res.header("Access-Control-Allow-Headers","Content-Type,Authorization,X-Requested-With")

//跨域允许的请求方式

res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS")

//执行下一步(跳转到下一个路由判断中)

next()

})

//开启服务,监听80端口

var server = app.listen(80,function(){

//当前当前监听的端口号

var port = server.address().port

console.log(`Server running at http://127.0.0.1:${port}/`)

})

5.代理服务器

http-server:启动时添加 -P 或者 --proxy 参数,当前服务器不能解析的资源会转交到代理服务器

//启动页面服务器8096,接口代理至8097

http-server -c-1 -p 8096 -P http://127.0.0.1:8097

//接口服务器

http-server -c-1 -p 8097

//页面代码(先访问 http://127.0.0.1:8096/data.json ,无法解析后再代理至 http://127.0.0.1:8097/data.json )

axios({url: "data.json"})

vue脚手架:配置方式不固定,一切以文档为主,代理配置

//vue.config.js

const { defineConfig } = require('@vue/cli-service')

module.exports = defineConfig({

transpileDependencies: true,

lintOnSave: false,

devServer: {

//代理

proxy: {

// 匹配以/api开头的所有路径

'/api': {

target: 'http://localhost:8097', // 代理的后端api域名

changeOrigin: true, // 支持跨域

}

}

}

})

6.Ajax跨域 + Cookie 认证

Ajax跨域 + Cookie 认证 要比token校验严格很多,例如

前端 不得自主编辑Cookie字段,只能让浏览器自动完成,例如:设置 withCredentials: true

后端 不能 使用 Access-Control-Allow-Origin: *,必须指定具体 Origin(如 http://localhost:8080 )

后端 必须显式设置 Access-Control-Allow-Credentials: true

自定义请求头(如 X-Requested-With)必须被后端在 Access-Control-Allow-Headers 中显式允许

鉴于每次换前端地址(如 dev/test/prod),后端 CORS 配置都需同步调整,开发环境不推荐使用 CORS 来处理 Cookie 跨域问题,建议使用服务器代理,即:

手动 Cookie 注入(先登录目标域名,拿到Cookie,再复制该 Cookie 到前端开发服务页面中)

开启服务器代理(通过服务器转发请求,绕过浏览器限制)

7.CORS 预检

预检是浏览器在真正发送跨域请求前,先发一个 OPTIONS 请求询问服务器:“这个跨域请求你允许吗?”只有预检通过,浏览器才会发送真正的请求

什么情况下会触发预检?

当请求满足 任一 以下条件时,就会触发预检:

使用了非“简单方法”

简单方法(不会触发预检):GET、POST、HEAD

非简单方法(会触发预检):PUT、DELETE、PATCH、TRACE、CONNECT

设置了非“简单头”

简单头(不会触发预检):

Accept

Accept-Language

Content-Language

Content-Type(仅限以下三种值):

application/x-www-form-urlencoded

multipart/form-data

text/plain

其他任何自定义头或 Content-Type(如 application/json)都会触发预检

为了区分何为自定义头,这里列出 浏览器自动设置的常见原生 Headers 列表

Header

是否可被前端修改

作用说明

Host

❌ 不可修改

指明目标服务器的域名和端口(由 URL 决定),HTTP/1.1 必需字段。

Connection

❌(通常为 keep-alive)

控制是否保持 TCP 连接复用。

Accept

⚠️ 可部分覆盖

声明客户端能接收的响应 MIME 类型(如 application/json, text/plain)。

Accept-Language

⚠️ 可覆盖

客户端偏好的语言(如 zh-CN,zh;q=0.9,en;q=0.8)。

Accept-Encoding

❌(通常为 gzip, deflate)

支持的压缩格式,由浏览器控制。

User-Agent

❌(现代浏览器禁止修改)

标识浏览器类型、版本、操作系统等。出于安全原因,现代浏览器已禁止前端修改。

Referer

❌(可被策略控制,但不可直接设值)

表示请求来源页面 URL(注意拼写是 Referer,历史遗留)。受 Referrer-Policy 影响。

Origin

❌(跨域时自动设置)

仅在跨域请求时出现,表示请求发起页面的源(scheme + host + port)。同源请求不发送此头。用于 CORS 校验。

Cookie

❌(自动携带匹配的 Cookie)

如果请求 URL 与 Cookie 的 Domain/Path 匹配,且未被 SameSite 阻止,浏览器会自动附加。前端无法读取 HttpOnly Cookie,也无法手动构造有效跨域 Cookie。

常见的自定义头:Authorization / Content-Type / X-Requested-With

前端拿到CORS 预检请求的相应,根据返回的相应确定是否进行下一步的正常请求

8.JSONP

原理:前端利用script不受同源策略影响,动态插入一个script标签,script标签加载完毕后会立即执行里面的代码

详细步骤:动态插入一个script标签并设定src地址(通过get参数告诉后端所需的接口参数和callbackFunction,callbackFunction要提前定义好),后端拿到此请求后,返回一段js代码,代码内容就是直接调用callbackFunction(),并传入数据,这样就完成了跨域请求,此种方法需要前后端同时配合

//后端代码 Node.js

//引入express

const express = require("express")

//创建服务对象

let app = express()

//设置路由,监听根路径的访问

app.get("/jsonp",function(req,res){

//获取回调名称

var jsoncallback = req.query.cb

//要返回的数据

var data = {title:"标题",content:"内容"}

//拼接代码(执行js函数调用)

var code = `${jsoncallback}(${JSON.stringify(data)})`

//返回数据

res.send(code)

})

//开启服务,监听80端口

var server = app.listen(8087,function(){

//当前当前监听的端口号

var port = server.address().port

console.log(`Server running at http://127.0.0.1:${port}/`)

})

Copyright © 2022 星战时空·网游活动中心 All Rights Reserved.