上次在项目中做一个手机验证码功能时遇到后端set-cookie
无法成功的问题,相信很多人都会遇到,今天分享出来,算是做个记录,也给有需要的小伙伴一个参考,下面先说一下场景
手机验证码实现步骤
用户输入手机号并点击发送验证码按钮
此时是向后端服务器发起高请求,传递手机号码
后端接收到手机号码后通过短信服务商的接口发送短信到手机号
由于安全性问题,一般服务商的接口都是由后端发起请求,那验证码是怎么来的呢?当然是我们自己后端生成的(一般是生产一个4位或6位随机数字号码),短信接口只负责发送
- 用户收到验证码后,在表单中输入并发送验证
- 后端收到验证码与第2步生成的进行校验,一致则通过,否则不通过
问题来了,我们都知道http请求是无状态的,即每个请求都是独立的,两个请求间没有任何联系,但上面的步骤中,用户发起了两次请求(第1步和第3步),后端怎么知道是同一个人呢?万一当时有10个人同时获取验证码,且都执行完所有步骤,怎么实现各自第1和第3步的对应关系进而进行第4步校验?于是我们引入session
什么是session
session
是保存的服务器的一个数据存储机制,一般用于存储用户状态数据,你可以理解为客户端的cookie
,因为它们很像,同样是有key/value
和有效时间,只是一个在服务端,一个在客户端。这跟本文有什么关系呢?肯定有关系,关系大了去了,不然我写干嘛(自嗨中~~~)
咳~咳。。回归正题,刚刚说了http是无状态的,而我们现在又要把第1步和第3步关联起来,怎么关联呢?session+cookie出马,具体原理如下
第一次接到请求,后关会生成一个
sessionid
来标识当前会话(我使用express-session
来实现),并通过set-cookie
响应头在客户端生一个cookie,大概长这样,connect.sid=s%3Au9xG34DBU1vOVbIpCax0neMxL_Uc1fIC.4ndNJL5G%2B41DtUSLbQ%2BW75Z9wduOAON4lfu2JGTDe5
- 由于cookie会自动发送给服务器,所以当前用户后面的所有请求都会携带这个
sessionid
给服务器,服务器通过这个sessionid
来标识是否为同一个用户,问题就迎刃而解了...
手机验证码功能具体实现步骤
前端:
- 用户输入手机号,点击获取验证码
- 发送请求到后端,
后端:
- 后端生成随机验证码,保存在
session
,并给前端响应connect.sid
(express-session会Set-Cookie
响应头) - 并通过手机接口发送给用户手机号(需要后端配置手机短信接口,一般需要购买,这里不做额外说明)
- 后端生成随机验证码,保存在
前端:
- 浏览器接收到
Set-Cookie
响应头后,自动把sessionid
写入浏览器cookie
- 用户接收到手机验证码并填写到对应输入框
- 用户点击按钮发送验证码到后端进行校验(
sessionid
随cookie
自动发送到后端)
- 浏览器接收到
后端:
- 后端拿到验证码,并与第3步保存在
session
的随机验证码进行比较,一致则响应成功,否则响应失败
- 后端拿到验证码,并与第3步保存在
通过以上步骤,正常情况下你已经能实现手机验证码功能了,可我偏偏遇到了非正常情况,这也是我今天这篇文章的意义,由于开发阶段为前后端分离,请求是产生了跨域,明明已经正确响应了Set-Cookie
,允许了CORS
跨域,但浏览器的cookie中就是看不到connect.sid
的身影,关键浏览器还不报错。
于是各种千里寻她千百度,最后发现默认情况下,标准的跨域请求是不会发送cookie等用户认证凭据的,同样,后端通过Set-Cookie
在跨域时默认是被浏览器忽略的,解决的方案是两步:
后端设置
"Access-Control-Allow-Credentials":true
响应头PS: 设置该响应头后,Access-Control-Allow-Origin的值不能设置为 *,必须设置为具体域名
- 前端发起请求时设置
withCredentials:true
请求头
// XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open('get',url,true);
xhr.withCredentials = true;
xhr.send()
// axios
axios.get(url,{
...
withCredentials:true
})
// fetch
fetch(url,{
...
credentials: 'include'
})
搞定,收工,希望对你有帮助
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。