前后端跨域问题
# 为什么会跨域?
- 谁出现的跨域? ==》 浏览器!
- 为何出现? ==》 同源策略
- 同源策略?
- 同源策略/SOP(Same origin policy)是浏览器最核心也最基本的安全功能,是浏览器对于ajax请求的一种安全限制:**一个页面发起的ajax请求,只能是与当前页同源的路径**,这能有效的阻止跨站攻击。Failed to load response data
- 所谓同源 ==》指的是 “协议+域名(ip地址)+端口” 三者相同,只要有一个不同就会导致跨域问题
- 目的:简单说就是禁止来自不同源的"document"或脚本,对当前"document"读取或设置某些属性。 在实际生成环境中,肯定会有很多台服务器之间交互,地址和端口都可能不同,怎么办?
- ** 解决方案? **
- 前端做代理服务器
- 跨域技术-
CORS
(CrossOrigin Resources Sharing
,跨源资源共享) (推荐)
CORS
,是HTML5
的一项特性,它定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。- 原理:浏览器一旦发现 axios 请求跨域,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感知。 服务器根据这些附加的值,决定是否同意此次请求。
JSONP
- 改变请求方式
dataType: 'jsonp', // 请求方式为jsonp
- ** 解决最优解** ==》
CORS
- 相比于
jsonp
只能用于get 请求来说cors
对于所有的请求都通用 jsonp
的优势在于可以在于支持老式浏览器,以及可以向不支持cors
的网站请求数据。
- 相比于
# 如何解决?
- 前端代理服务器(vue)
- vue.config.js ==> 配置文件
// 配置代理
devServer: {
port: 9528,
open: false,
overlay: {
warnings: false,
errors: true
},
proxy: {
"/dev_api": { // 匹配所有以 '/dev-api'开头的请求路径
target: "http://localhost:8800/", ==》 此处写明自己应该访问的路径
changeOrigin: true,// 支持跨域
pathRewrite: {// 重写路径: 去掉路径中开头的'/dev-api'
"^/dev_api": ""
}
}
}
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- request.js 中baseUrl
let instance = axios.create({
baseURL: "/dev_api",
withCredentials: true, // send cookies when cross-domain requests
timeout:5000,
})
// 注意: dev_api 之前的 “/” 一定不可省略 别问我为啥知道的
// 此处的dev_api 要与上面的相对应 当然 也可以起自己的名字
1
2
3
4
5
6
7
2
3
4
5
6
7
效果:即前端将访问9528的请求改为8800的请求发送
或前端直接:
因为
直接
中将base_API改为要访问的路径
- 跨域技术-
CORS
(推荐)
- 简介
- CORS 通信过程都是浏览器自动完成的 不需要用户参与
- 对于开发者一样,CORS 通信与普通的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨域,就会自动添加一些附加的头信息
- 因此 实现CORS的关键是服务器,只要服务器实现了CORS接口 就可以跨域通信
- 简单请求(get post)
- 当浏览器发现跨域之后,就会向请求头信息里面自动添加一个
Origin
字段
- 当浏览器发现跨域之后,就会向请求头信息里面自动添加一个
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
...
1
2
3
4
5
2
3
4
5
- `Origin`字段说明了本次请求来自哪个域(协议+域名+端口)
- 如果发现 `Origin`为指定的源 (白名单中),服务器会响应成功,并在响应头中多几个信息字段
- (服务端)设置响应头:
// 该字段是必须的 该字段要么* ,要么是请求时 Origin 中的值
Access-Control-Allow-Origin: http://api.bob.com
// 该字段可选 是否允许发送cookie
Access-Control-Allow-Credentials: true
1
2
3
4
2
3
4
- 如果发现 `Origin`不在许可的范围内,服务器会返回一个正常的Http回应200,但Response 中没有内容,显示 Failed to load response data,控制台并且抛出错误 XMLHttpRequest...
- 前端如何配置?
- CORS的关键是服务端 前端只需要配置
withCredentials
属性 - 跨域请求,默认是不发送这个 Cookie 的 如果我们想要包含就必须在请求中将
**withCredentials**
属性 配置为true
- CORS的关键是服务端 前端只需要配置
let instance = axios.create({
baseURL: "/dev_api",
withCredentials: true,
timeout:5000,
})
instance.defaults.withCredentials = true;//允许携带cookie ==> 这个太关键
1
2
3
4
5
6
2
3
4
5
6
- 同样 需要注意的是 如果后端要发送cookie
Access-Control-Allow-Origin
就不可以设置为 * !! 也就意味这必须指定明确的、与请求网页一致的域名
# 实现
- 浏览器端都有浏览器自动完成,我们无需操心
- 服务端可以通过拦截器统一实现,不必每次都去进行跨域判定的编写。
或者使用CorsFilter
# Token 、Cookie 与 CORS 的爱恨情仇
- 用户验证何来?
- 众所周知,当客户端多次向服务端请求数据时,服务端就需要多次从数据库中查询用户名和密码并进行对比,判断用户名和密码是否正确,并作出相应提示。
- 但这样无疑会增加服务器端的运行压力,是否可以有一种方式只需要验证用户就是之前的用户而不需要每次在客户端请求数据时都需要查询数据库判断用户名和密码是否正确。
- 就这样 为了避免大量的查询数据库 减小服务器端的运行压力 令牌机制应运而生
- 令牌如何运作?
- token
- 前端登录成功后 后台返回token
# 前端要对token 进行处理 存储并放到请求头中 每次请求都会将token 放入 请求头中
- token
instance.interceptors.request.use(
(config) => {
if (!config.noAuthorization) {
let token = RootStore.userStore.allData.accessToken;
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
}
return config;
},
(err) => {
console.log(err);
},
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cookie 中存储
- 后端将令牌(sessionId) 存到浏览器的Cookie 中
- 前端请求自动从cookie 将sessionId 传到后端
当 token、cookie 遇到跨域 🤦
- token
- 跨域了? 我们可以直接在服务端
Access-Control-Allow-Origin
就设置为 * 前端withCredentials
属性为false
- 也就是说 允许所有的 origin 访问
- 跨域了? 我们可以直接在服务端
- cookie
- 理论上 我们可以遵循token的方法进项解决,但
- 上文所说 ==》需要注意的是 如果要发送cookie
Access-Control-Allow-Origin
就不可以设置为 * !! 也就意味这必须指定明确的、与请求网页一致的域名 - 也就意味这 ,我们需要指定明确的 域名 进行访问 并且要告诉后端 我们的的域名是什么,并让他加入白名单中
# 最重要的 我们要在前端配置
withCredentials
属性 配置为true
- token
let instance = axios.create({
baseURL: "/dev_api",
withCredentials: true,
timeout:5000,
})
instance.defaults.withCredentials = true;
1
2
3
4
5
6
7
2
3
4
5
6
7
- 同时在后端配置 `access-Control-Allow-Credentials` 为 `true`
- 这样在 所给的的origin url之下 我们就可以实现登录 并解决跨域
- 小问题
- 只有用浏览器访问服务器地址下的路径,浏览器才会自动带上cookies,那么我们本地调试带不上cookie怎么办?
- 方案一: 利用服务器代理 ==》 利用上述服务器代理解决跨域的问题
- 缺点:但是上服务器的时候需要进行修改 baseURL
- 方案二:利用服务器上已经传入的sessionId 对本地进行写入 ==> 控制台
- 没有的话进行写入
- 缺点:sessionId 更新时间较短不适用 需要频繁的修改写入 sesionId
- 总结
- 由篇幅量可得,token 比 cookie 便捷 事实也是 cookies有很多限制和麻烦 就导致比较的麻烦
- 对于安全性来讲:两者都是用令牌进行传送 ,只不过方式不一样 ,安全性还是差不多的
参考文献:
编辑 (opens new window)
上次更新: 2023/05/26, 15:58:27