目录
HTTP协议的无状态性
HTTP协议三大特点
HTTP协议无状态性的优缺点
HTTP协议无状态性的解决方案
Cookie
Cookie是啥
Cookie技术的具体工作流程
expires和max-age的区别
expires取值要求
max-age取值要求
expires和max-age的优先级
domain
path
Cookie实现身份认证小案例
需求描述
需求实现
“请求应答模式” : 每个HTTP请求都必然对应一个HTTP响应。
“无连接” :客户端在发送HTTP请求前需要与服务器建立连接,而在收到服务器的HTTP响应后,就会断开连接,即一次连接只支持一次HTTP请求应答。无连接可以理解为HTTP协议不会持久维持连接。
“无状态”:HTTP协议本身不支持每次HTTP请求应答的状态的保存,只负责状态的传递。无状态可以理解为HTTP协议无法保存状态。
HTTP是一种简单(请求报文,响应报文结构简单清晰,无状态性),灵活(请求头,响应头容易扩展),对服务器友好(无连接特性,不会持久占用服务器的连接资源)的协议。
无状态性 保证了HTTP协议的简单性。假设我们需要HTTP协议支持HTTP请求应答的状态的保存,那么HTTP协议至少需要考虑如下问题:
而HTTP协议加入了以上需求,必然导致HTTP协议设计的复杂性变高,使用难度变大。
那么HTTP协议的无状态性的缺点是啥呢?
在业务上关联的两次HTTP请求应答,比如后一次HTTP请求需要前一次HTTP响应的数据。由于HTTP无状态性,前一次的HTTP响应数据无法被保存,所以后一次HTTP请求也无法获得。对应的业务场景就是身份认证。
解决方案很简单,既然HTTP协议本身不支持保存状态,那么就在浏览器和服务器实现状态的保存。下面示例是浏览器与服务器对于登录状态的保存:
分析上面流程,可得:
浏览器需要支持:登录状态的保存,以及将登录状态加入HTTP请求
服务器需要支持:登录状态的生成,以及对登录状态的验证
以上这种HTTP无状态性的解决方案就是Cookie技术。
Cookie是浏览器端创造的,用于解决HTTP协议无状态性的技术。
浏览器与服务器约定,如果某次HTTP响应中的状态需要保存,则
我们这里将“状态”简称为cookie。
总结可得:
cookie可以分为两部分内容,一部分是cookie具体内容,一部分是cookie的描述属性
cookie的具体内容就是一个键值对,表现为 cookie_name=cookie_value形式
而cookie的描述属性,主要用于描述cookie的生命周期,作用范围,以及安全限定,包含如下
expires | cookie失效日期,不设置,表示cookie生命周期是本次会话 |
max-age | cookie存活时间,不设置,表示cookie生命周期是本次会话,当同时存在expires和max-age时,以max-age为准 |
domain | cookie作用域,默认为服务器域名 |
path | cookie作用路径,默认根路径“/” |
httponly | cookie仅用于http请求,不可通过javascript获取 |
secure | 浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。 |
samesite | 跨站cookie是否发送
|
cookie由服务器端生成,服务器端通过HTTP响应头Set-Cookie将cookie传递给浏览器保存。
浏览器支持解析的cookie格式为 cookieName=cookieValue;cookiePropName=cookiePropValue
如下示例
上面代码,生成了一个cookie,cookie名为用户名,cookie值为密码,cookie存活时间为60s,cookie作用域为qfc.com及其子域,cookie作用路径为根路径,cookie不允许跨站
cookie由浏览器端保存,浏览器请求服务器/login接口后,可以自动从HTTP响应头Set-Cookie获取cookie值
浏览器会将解析到cookie值自动保存在内存或硬盘中
当浏览器再次发送HTTP请求到qfc.com及其子域时就会自动携带该cookie值(前提是cookie未失效)
cookie有两个属性控制有效期,分别是expires和max-age,二者作用是相同的,只是取值不同。
expires取值为标准的日期字符串,浏览器会解析日期字符串作为cookie失效日期,服务器可以使用Date.prototype.toUTCString()获取标准的日期字符串。注意一定要是标准的日期字符串,否则浏览器会解析失败。
max-age取值为正整数,默认单位为秒,表示cookie被浏览器保存后存活的时长。
如果同时设置了expires和max-age,则以max-age为准
可以发现max-age设置了30s失效,expires设置了60s失效,最终以30s失效为准,这里相差不足30s是因为我截图慢了。
cookie的有效期主要影响了
如果我们通过expires或max-age设置了cookie有效期,则浏览器端会将cookie保存在硬盘中,当有效期到了后,浏览器端就会自动清除硬盘中的失效cookie。
如果我们未设置expires和max-age值,则浏览器端会将cookie保存在内存中,cookie的生命周期是本次会话,即浏览器关闭时cookie会被清除。
可以发现此时cookie生命周期值为Session,表示会话生命周期
我们需要注意的是会话生命周期是浏览器关闭为结束点,而不是当前网页关闭。实际上会话级别cookie保存在浏览器进程的内存中,当浏览器关闭,意味着浏览器进程的死亡,以及进程所占用的内存,CPU的释放,因此保存在浏览器进程内存中的cookie也会被释放。
cookie的domain属性旨在帮助浏览器认识当前cookie用于哪些域名或IP地址对应的服务器,但是服务器端设置cookie的domain以及浏览器端使用cookie的domain需要注意如下
服务器HTTP响应头Set-Cookie中domain设置是否合法依据如下:
通过如上设置,服务器IP地址为192.168.3.9,对应域名为www.qfc.com。则服务器响应头中Set-Cookie的domain属性合法值为www.qfc.com或者qfc.com,其他都为非法值。
以上domain为非法值,浏览器端会解析失败
以上domain为合法值,浏览器端成功解析后保存
浏览器发送http请求是否携带cookie需要根据cookie的domain属性:
以上验证的是虽然cookie的domain为qfc.com,但是当浏览器请求www.qfc.com时,依旧会携带属于qfc.com下的cookie
domain默认值
如果服务器HTTP响应头Set-Cookie中不包含domain,则domain默认取值服务器所在域名或IP地址
domain为域名时取值要求
服务器端HTTP响应头的Set-Cookie的domain值不能是顶级域名 . 或一级域名如com、cn等,也不能是公开域名如github.io
cookie的path值为一个资源路径,它需要结合cookie的domain属性使用,这样path就能帮助浏览器识别当前cookie可以用于服务器的哪些资源了。
需要注意的是cookie可以用于path指定路径下的所有资源,path的默认值为根路径“/”,表示浏览器访问服务器所有资源时都需要携带cookie。
Cookie技术诞生的初衷是为了解决HTTP协议无状态性引发的用户登录状态无法保存的问题。
而用户登录状态属于用户敏感的隐私数据,一旦被泄漏或者盗取或者伪造,则会造成用户信息财产的损失。所以cookie的安全一定要得到保障。
而cookie的安全性问题主要来自于浏览器端。
Cookie就是浏览器端创造的技术,浏览器端也提供了一个操作cookie的方式 document.cookie,通过document.cookie就可以在浏览器端通过javascript进行cookie的增删改查
首先,document.cookie操作的是当前网页所在域名(及其父域)和路径的cookie
查询
新增
新增操作即直接给document.cookie赋值
修改
修改操作也是给document.cookie赋值,但是赋值时需要保证cookieName,expires/max-age,domain,path必须和要修改的cookie相同,cookieValue和要修改的cookie不同。
删除
删除操作本质也是修改操作,主要是改变对应cookie的max-age属性为0,或者expires属性值为一个过期时间,这样浏览器就可以自动清除失效的cookie了。
以上是document.cookie对于cookie的基本操作。
下面我们通过DOM型XSS注入脚本来盗取他人cookie,比如常见的网站评论中,经常有人在评论区发一些奇怪的链接诱惑其他人去点击 比如上面例子,该评论区没有做用户输入过滤,导致网页被注入了一个a标签,a标签被click就会发送一个fetch请求,该fetch请求会将当前网页的document.cookie发送到http://127.0.0.1/heike接口。
当该包含恶意代码的评论被别人点击后,就会把自己的cookie信息发送给黑客服务器
为了避免这种情况,cookie有一个属性httponly可以禁止javascript操作cookie,比如使用document.cookie操作cookie。
当cookie设置了httponly后,则无法通过document.cookie操作httponly限定的cookie了
目前http协议已经不是一种推荐的安全协议,因为http协议在传输层没有对报文进行加密,这样就有可能造成网络传输中http请求被拦截,然后直接解析出明文的cookie。
而https在传输层对报文使用了证书公钥进行加密,解密只能依赖服务器独有的证书私钥才可以,所以https协议不担心网络传输过程中报文被破解的问题,除非证书私钥泄漏。
为了保护cookie不被以明文的方式在网络中传输,我们可以设置cookie的secure属性,表示cookie只有在https协议下才允许被使用,在http协议下禁止使用。
但是目前goole内核浏览器,已经禁止了https网页发送http请求
从上面结果可以看出https://www.qfc.com/other.html被禁止请求http://test.qfc.com/heike,原因是https网页必须请求https服务器接口
所以其实cookie的secure属性功能已经被浏览器涵盖了。
我们还需要注意cookie的secure属性的一个情况,那就是如果服务器cookie设置了secure,但是以http协议传输给浏览器,则浏览器会拒绝存储此cookie
跨站请求和跨域请求
当浏览器网页 与 服务器接口 的 协议,主机,端口任一个不同时,浏览器网页发送给服务器接口请求就是跨域请求。
当浏览器网页 与 服务器接口 的 主机 不同,浏览器网页发送给服务器接口的请求就是跨站请求。
分析可得:
跨站请求就是跨域请求,而跨域请求不一定是跨站请求。
跨站请求与cookie
情况 | 浏览器网页所在域 | 服务器接口所在域 | cookie携带情况 |
同源请求 | http://www.qfc.com/ |
http://www.qfc.com/ | 携带domain为www.qfc.com和qfc.com的cookie |
同站请求 | http://www.qfc.com/ | http://www.qfc.com:8080/ | 携带domain为www.qfc.com和qfc.com的cookie |
同父域站点请求 | http://www.qfc.com/ | http://test.qfc.com/ | 携带domain为qfc.com的cookie |
跨站请求 | http://www.qfc.com/ | http://127.0.0.1/ | ? |
跨站请求是否携带cookie受到cookie的samesite属性影响,cookie的samesite属性有如下取值:
当cookie的samesite=strict时,表示浏览器禁止跨站请求携带cookie
当cookie的samesite=none时,表示浏览器允许跨站请求携带cookie,但设置samesite=none的前提是,cookie的secure属性打开,即samesite=none的cookie只能用于https请求
当cookie的samesite=lax时,表示浏览器禁止了绝大部分跨站请求携带cookie,只允许少量导航到目标网址的 GET 请求可以携带cookie,具体如下:
服务器代码如上,若要/pay,则必须要有登录cookie
如上,GET表单 跨站请求 可以携带跨站cookie
如上,超链接 跨站请求 可以携带跨站cookie
需要注意的是以上跨站cookie包含了父域cookie
上面的基于超链接和GET表单本质都是通过网页刷新发起的GET请求,所以只要是它们都算是导航到目标网址的GET请求。
另外对于打开了secure和samesite=none的cookie来说,理论上支持所有跨站请求携带
但是实际上似乎已经不行了
所以目前,整体而言,浏览器已经不支持非导航性质的跨站请求携带cookie了。
xhr,fetch,axios默认同站请求时携带cookie,跨站请求时不携带cookie,如果想要跨站请求携带cookie,则需要:
但是由于浏览器已经不支持非导航性质的跨站请求携带cookie了,所以上述操作无效。
共三个页面:首页index.html,登录页login.html,注册页register.html
首页需求:对用户登录cookie进行认证,
认证成功则显示“欢迎xxx”,并附带退出登录按钮;
登录页需求:包含三个用户输入,分别是用户名,密码,七天免登录,通过提交按钮可以提交三个用户输入信息,另外附带了注册页超链接
注册页需求:包含两个用户输入,分别是用户名,密码,通过提交按钮可以提交,另外附带登录页超链接
cookie身份认证小案例-Node.js文档类资源-CSDN文库
0积分下载,大家安心使用
数据库使用的mongoose,基于如下代码进行数据库,集合的创建。首先使用mongo命令进入mongo shell,然后执行如下代码
use usermgr
db.user.insertOne({name:'test', pass:'test'})
即可完成数据库,集合创建
代码中,主要需要修改的地方是db/index.js中数据库ip,以及web/*.html中fetch请求的URL地址,将他们改为自己本地的即可。
module.exports = function(options) {
return function(req, res, next) {
req.cookies = {}
if(req.headers && req.headers.cookie) {
let cookieStr = req.headers.cookie
let cookieItems = cookieStr.split(';')
cookieItems.forEach(item => {
let [key, value] = item.trim().split('=')
req.cookies[key] = value
})
}
next()
}
}