1、什么是跨域
请求同域资源:
在协议,域名 (或 ip 地址)相同,端口号相同下的请求资源,可以看做是同域资源
请求跨域资源:
请求的资源只要 是协议 、域名(或 ip 地址)、端口号中任意一个不同的资源都可以认为是跨域资源
如:
端口号不同
127.0.0.1:80 与 127.0.0.1:8888 属于跨域
域名 (或 ip 地址) 不同
127.0.0.1:80 与 www.baidu.com:80 属于跨域
2、前后端分离项目中的跨域问题
例如:
前端 vue项目: 127.0.0.1:70
后端 SpringBoot项目: 127.0.0.1:8888
我们在前端 vue 项目中存放页面,页面中的数据是通过 axios 发起异步请求从 后端 SpringBoot项目中获取的
因此当我们访问前端页面时候, 我们可以理解为所在的当前所在 127.0.0.1:70 域上,所以访问这个127.0.0.1:70 域 的资源都是OK的,但如果使用 js 的方法访问其他域的资源时 就会出现跨域问题
本次教程拿 验证码 举例:
浏览器输入 127.0.0.1:70 访问登录资源,在 login.vue 中的 created 方法中调用 获取验证码方法 ( 发起axios 请求 127.0.0.1:8888 )
此时就会出现跨域的问题
3、方法一:SpringBoot后端进行处理
在 每个 Controller 类上加入 @CrossOrigin 注解
或者
在 Controller的基类中加上 @CrossOrigin 注解然后其他 Controller 类就有了这个 @Controller
此时跨域访问就不会报错了。
login.vue 访问的后端地址:
@CrossOrigin 注解解决方案的优缺点:
优点 :
- 使用起来简单,直接在Controller类上加 @CrossOrigin 注解即可
缺点:
- 如果后端技术使用的不是 SpringBoot,后端代码还需要处理跨域问题
- 浏览器直接访问 后端API,在某种程度上是不太安全的
4、方法二:在Vue前端进行处理(仅限开发环境)
浏览器 在同一个页面访问不同的域 是存在跨域问题的
但 服务器之间的访问是 没有跨域问题
因此 需要在前端设置代理, 通过代理访问 SpringBoot后端API
要是直接在代码里请求http://127.0.0.1:8888/api/captcha/captchaImage?type=math是会有跨域限制的,我们使用代理的方式来绕过,请求的时候也不必给出完整的请求地址(见4.4)
4.1 安装
cnpm install axios
4.2 配置请求 baseURL
在 src / utils / request.js 中
// 创建axios实例
const request = axios.create({
baseURL: '/api',
timeout: 10000
})
这样会为所有的 请求url 前面都加上 ‘/api’,方便做 统一代理
4.3 配置 proxy
在 vue.config.js 中
module.exports = {
devServer: {
// 自动打开浏览器
open: true,
port: 70,
proxy: {
'/api': {
target: `http://localhost:8888/ruoyi`,
changeOrigin: true,//开启代理
pathRewrite: {
'^/api': ''
}
}
}
}
}
4.4 login.vue 页面发送验证码的请求
请求 API
import request from '@/utils/request'
// 获取验证码
export function getCodeImg() {
return request({
url: '/captcha/captchaImage?type=math',
method: 'get'
})
}
相当于请求遇见 /api 才做代理,但真实的请求中没有/api,所以在pathRewrite中把 /api 去掉, 这样既有了标识, 又能在请求接口中把 /api 去掉
注意
pathRewrite:如果不写则只能修改代理的域名,如果写则可以修改代理的域名和后边的路径
请求的截图:
我们发现 请求验证码的地址是前端服务器的地址
http://localhost:70/api/captcha/captchaImage?type=math
但我们配置了 proxy
proxy 解决跨域问题优缺点
优点:
- 在浏览器中屏蔽了实际访问后端的 地址,相对安全
- 后端代码不必要进行额外处理跨域
缺点:
- 在浏览器中看不到后端访问的地址,开发阶段调试不太方便
5、方法三:使用 nginx代理
前边部分的代码设置主要用来设置node服务的代理解决跨域问题的,打包后的前端项目,就是一个html和对应的js。要想独立部署又要解决跨域问题,就一定要从web容器的设置上下手。
前半部分的设置和 方法二 一样,之后对代理的设置如下:
# 新增的服务
server {
listen 8086; # 监听的端口
server_name 47.106.197.14; # 机器ip
location / {
root /home/; # vue 打包后静态文件存放的地址
index index.html; # 默认主页地址
}
location /api {
proxy_pass http://47.106.197.89:9096/; # 代理接口地址
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
6、proxy匹配规则
在 proxy 中可以写多个代理地址
如
devServer: {
proxy: {
'/user': {
//要访问的跨域的api的域名
target: 'http://www.user.com',
changOrigin: true,
},
'/order': {
target: 'http://www.order.com',
changOrigin: true,
},
'/pay': {
target: 'http://www.pay.com',
changOrigin: true,
},
}
}
proxy 的匹配规则是根据正则匹配 如上 /user、/order、/pay 如果 请求地址 包含 此字符串,就算匹配成功,一旦匹配成功就 不会 继续向下匹配
如:
- 请求 /user/info 则匹配 target 为 http://www.user.com
- 请求 /order/user/list 则匹配 target 也为 http://www.user.com
- 请求 /order/pay/user/list 则匹配 target 也为 http://www.user.com
- 请求 /pay/order/list 则匹配 target 为 http://www.order.com
如果我们需要以 某个路径开头 才算 匹配成功,则可以改成:'^/user'、'^/account'、'^/pay
7、结合多axios实例的配置
结合之前的关于多实例的博客,在项目中可以这样配置
//main.js
var instance1 = axios.create({
baseURL: '/api1',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
var instance2 = axios.create({
baseURL: '/api2',
timeout: 2000,
headers: {'X-Custom-Header': 'foobar'}
});
var instance3 = axios.create({
baseURL: '/api3',
timeout: 3000,
headers: {'X-Custom-Header': 'foobar'}
});
Vue.prototype.$http1=instance1 ;
Vue.prototype.$http2=instance2 ;
Vue.prototype.$http3=instance3 ;
devServer: {
proxy: {
'/api1': {
target: 'http://127.0.0.1:8888/projectA',
changOrigin: true,
pathRewrite: {
'^/api1': ''
}
},
'/api2': {
target: 'http://127.0.0.1:8889/projectB',
changOrigin: true,
pathRewrite: {
'^/api2': ''
}
},
'/api3': {
target: 'http://127.0.0.1:8899/projectC',
changOrigin: true,
pathRewrite: {
'^/api3': ''
}
},
}
}
//使用第一个实例
this.$http1({
method: 'post',
url: 'consumer/trace',
data: {
openId: "oEy5O1231312321315sk",
userId: "2597123134300"
}
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
url的变化如下 consumer/trace --> api1/consumer/trace --> 小型服务器 (api1/consumer/trace --> /consumer/trace --> http://127.0.0.1:8888/projectA/consumer/trace) --> 后台
其他的两个接口同理