cookie cors
cookie
技术背景
http
协议是一个无状态的协议,无状态就意味着服务器不知道这次请求的人,跟之前请求的人是否是同一个。
所以cookie
出现的主要应用于记录用户的部分信息,这部分信息让客户端存储起来,然后每次请求的时候将这部分信息带给服务端,这样服务端就能区分用户的神分信息,还有一些偏好设置。
场景
假设服务器有一个接口,通过请求这个接口,可以添加一个系统管理员,但是,并不是系统中的用户都有权力进行这个操作,由于http
协议无状态的特性,所以就需要用到一种身份验证的解决方案。这个解决方案就是cookie
具体方案
- 客户端登录成功之后,服务器会颁发一个令牌给客户端
- 后续客户端每次请求,都需要携带这个令牌
cookie的组成
cookie是浏览器中特有的一个概念,他就像浏览器的专属卡包,管理着各个网站的身份信息。每一个cookie就相当于是某个网站的一个令牌,它记录了这么些信息:
key:键,比如 身份编号
value:值,可以是任何信息
domin:域,表达这个cookie所属的网站,比如clesbit.top,表示这个cookie是属于clesbit.top这个网站的。
path:路径,表示这个cookie是属于该网站的哪个基路径的,就好比同一家公司不同的部门的权限一样,不同的部门的权限不一样。比如
/path
表示这个cookie属于这个 /path
路径
- secure:是否使用安全传输
- expire:过期时间
当浏览器向服务器发送一个请求的时候,他会自动携带合适的令牌带给服务端,如果一个cookie同时满足以下的条件,则这个cookie会被附带到请求头当中
- cookie没有过期
- cookie中的域和这次请求的域是匹配的
- 比如cookie中的域是clesbit.top,则可以匹配的请求域名:www.clesbit.top 、blogs.clesbit.top
- 比如cookie中的域是www.clesbit.top,则只能匹配www.clesbit.top这样的请求域
- cookie是不在乎端口的,只要域匹配即可
- cookie中的path和这次请求的path是匹配的
- 比如cookie中的path是
/admin
,则可以匹配的请求路径是/admin
、/admin/detail
、/admin/a/b/c
等,但是不能匹配/blogs
- 如果cookie的path是
/
,可以匹配所有的路径
- 比如cookie中的path是
- 验证cookie的安全传输
- 如果cookie的secure属性是true,则请求协议必须是https,否则不会发送该cookie
- 如果cookie的secure属性是false,则请求的协议可以是http,也可以是https
如果一个cookie满足了上述的所有条件,则浏览器会自动把他加入到这次请求中
具体加入的方式是,浏览器会将符合条件的cookie,自动放置到请求头当中,例如,当我访问语雀的时候,它自动在请求头中附带了下面的cookie
属性值
path
设置cookie的路径,如果不设置,浏览器会将其自动设置为当前请求的路径。比如,浏览器请求的地址是
/login
,服务器响应了一个set-cookie:a=1,浏览器会将该cookie的path设置为请求的路径/login
一定程度上,path的值和请求的地址还是有一定的关联性的。
domain
设置cookie的域。如果不设置,浏览器会自动将其设置未当前的请求域,比如,浏览器请求的地址是http://www.clesbit.top,服务器响应了一个set-cookie:a=1,浏览器会将该cookie的domain设置为请求的域www.clesbit.top
- 如果服务器响应了一个无效的域,浏览器是不认可的,比方说,浏览器请求的域是www.baidu.com,服务器响应的cookie是set-cookie:a=1; domain=www.alibaba.com,这样的域浏览器是不认可的。如果浏览器允许这样做,那么就会出现以下情况:百度的服务器可以颁发阿里服务器的令牌,用户可以通过百度的颁发给的令牌去访问阿里的服务器。
expire
设置cookie的过期时间。这里必须是一个有效的GMT时间,即格林威治标准时间,比如
Fri,17 Apr 2024 12:22:59 GMT
,表示格林威治时间的2024-04-17 12:22:59
,即北京时间2024-04-17 20:22:59
。当客户端的时间达到这个时间点后,会自动销毁该cookiemax-age
设置cookie的相对有效期,expire和max-age通常仅设置一个即可。比如设置max-age为1000,浏览器在添加cookie时,会自动设置他的expire为当前时间加上1000秒,作为过期时间
- 如果不设置max-age和expire,则表示会话结束后过期
- 对于大部分浏览器而言,关闭所有浏览器窗口意味着会话结束
secure
设置cookie是否是安全连接。如果设置了这个值,则表示该cookie后续只能随着https请求发送。如果不进行设置的话,表示cookie会随着所有请求发送到服务端
httponly
表示这个cookie仅仅用于服务器,js脚本无法获取到这个字段
一段node服务端代码
1 | router.post("/login", async (req, res) => { |
跨域
:warning:跨域的主要原因是浏览器的同源策略。
JSONP跨域解决
原理是script标签的加载不受同源策略的限制
客户端部分代码
1 | <script src="./js/index.js"></script> |
服务端部分代码,服务端将请求的数据以js脚本的形式响应给客户端,客户端调用对应的回调函数即可拿到数据。
1 | router.get("/", async (req, res) => { |
缺点:
验证影响服务器的响应格式
仅局限于get请求
CORS
概述
cors
是基于http1.1的一种跨域解决方案,全称是cross-origin-resource-sharing
,跨域资源共享。解决跨域问题的思路如下:
当某个网站发起请求,浏览器会放行,让请求到达指定的服务器,但是响应回来的时候,浏览器就会做同源策略的检查,如果不符合同源策略,他就会给你拦截下来,但是如果服务器的响应中表明自己,能够被该网站访问。那浏览器就不会拦截这个请求的响应。
- 服务器:对的他是我的朋友,让他接到我的回应吧
- 浏览器:好的
- 用户:nice
但是往往一个请求的处理的流程总是复杂的,不同的请求方法,对于服务器的影响程度是不同的。
针对不同的请求,CORS规定了三种不同从处理模式
- 简单模式
- 需要预检的请求
- 附带身份凭证的请求
从上往下请求处理的模式越来越复杂,对于相关的条件也越来越严格。
简单请求的判定(同时满足以下的条件)
1. 请求方法属于下面的一种
- get
- post
- head
2. 请求头仅包含安全的字段,常见的安全字段如下:
- Accept
- Accept-Language
- Content-Language
- Content-Type
- DPR
- DownLink
- Save-Data
- Viewport-width
- Width
3. 请求头如果包含Content-Type,仅限下面的值之一:
- text/plain
- mutipart/form-data
- application/x-www-urlencoded
eg:
1 | // 简单请求 |
当浏览器判定某个ajax跨域请求是简单请求的时候,会发生下面的事情。
- 会在请求头自动添加Origin字段,这个字段表明请求来自哪一个域
- 服务器响应头中应包含Access-Control-Allow-Origin,这个字段就表明服务器允许的访问的源
需要预检的请求
简单的请求一般对于服务器的影响不大,所以简单请求的交互模式比较简单,但是如果浏览器认为这个不是一个简单的请求,就会按照下面的流程进行:
- 浏览器发送预检请求,询问服务器是否允许
- 服务器允许
- 浏览器发送真实的请求
- 服务器完成真实的响应
例如
1 | //需要预检的请求 |
特别注意
预检请求示例
1 | OPTIONS /api/student HTTP/1.1 |
预检请求是没有请求体的,它包含后续真实请求要做的事情
预检请求有如下的特征:
- 请求方法未OPTIONS
- 没有请求体
- 请求头中包含
- Origin:请求的源,和简单请求的含义一致
- Access-Control-Request-Method:后续的真实请求将使用的请求方法
- Access-Control-Request-Headers:后续真实请求会改动的请求头
服务器收到预检的请求之后,可以检查预检请求中包含的信息,如果允许这样的请求,需要响应下面的消息格式
一断服务端处理预检请求的代码
1 | //这是一个中间件,专门用于处理跨域请求的 |
1 | HTTP/1.1 200 OK |
后续请求和简单请求差不多。
附带身份凭证的请求
Credentials(凭据)
默认情况下,ajax的跨域请求并不会附带cookie,这样一来,某些需要权限的操作就无法进行,不过可以通过简单的配置就可以实现附带cookie
1 | //xhr |
这样一来,该跨域的ajax请求就是一个附带身份凭证的请求,当一个请求需要附带cookie的时候,无论他是简单请求,还是预检请求,都会在请求头中添加cookie字段。
而服务器响应的时候,需要明确告知客户端:服务器允许这样的凭据。
告知的方式也很简单,只需要在响应头中添加:Access-Control-Allow-Credentials:true即可
对于一个附带身份凭据的请求,如果服务器没有明确的告知,浏览器依然会把其视为跨域。
对于附带身份凭证的请求,服务器不得设置Access-Control-Allow-Origin的值为*