1. 诞生这些玩意的原因
HTTP是一种没有状态的协议,也就是它并不知道是谁是访问应用。
而当下许多应用程序是十分依赖当前协议的,比如:京东,咱们上这个网站购买物件时,选好物件付钱时,服务器是需要知道我们是谁。试想一下,我们刚付完钱,刷新一下,哦豁,服务器又不知道你是谁了,相当你购买的东西被白嫖了不是。
如果我们可以每一次刷新页面或者加载页面时都可以携带一个凭证,我们每一次刷新都告诉一次服务器,那这样服务器不就知道我是谁了么,那怎么携带?每次都手动操作么?如果是这样,用户买个东西还不得累趴下,有这点时间还不如去超市买东西,还锻炼了身体,何乐而不为呢,何必让自己难受。
难道真的束手无策了么,不用了?还是说换一个协议得了?自然不是了,Lou Montulli
大神发明了一种神奇的饼干,把这块小饼干给浏览器吃了,然就拥有了记忆,神不神奇,咳咳咳,扯远了,这块小饼干就是cookie
。而这个东西的发明,在当时是解决电商网站的交易问题的。
2. cookie
2.1 介绍
cookie存储在用户机器上,它由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。
2.2 cookie 的生命周期
Cookie 的生命周期可以通过两种方式定义:
会话期 Cookie 是最简单的 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。会话期 Cookie 不需要指定过期时间(Expires)或者有效期(Max-Age)。需要注意的是,有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期 Cookie 也会被保留下来,就好像浏览器从来没有关闭一样,这会导致 Cookie 的生命周期无限期延长。
持久性 Cookie 的生命周期取决于过期时间(Expires)或有效期(Max-Age)指定的一段时间。
2.3 cookie 的限制访问
Cookie 的限制访问可以通过两个属性设置(Secure, HttpOnly):
标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端,因此可以预防 man-in-the-middle 攻击者的攻击。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障
JavaScript Document.cookie API 无法访问带有 HttpOnly 属性的 cookie;此类 Cookie 仅作用于服务器。例如,持久化服务器端会话的 Cookie 不需要对 JavaScript 可用,而应具有 HttpOnly 属性。
2.4 cookie 的作用域
Cookie 的作用域可以通过两个属性控制(Domain, Path):
Domain 指定了哪些主机可以接受 Cookie。如果不指定,默认为 origin,不包含子域名。如果指定了Domain,则一般包含子域名。因此,指定 Domain 比省略它的限制要少。但是,当子域需要共享有关用户的信息时,这可能会有所帮助。
Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。
2.5 cookie的SameSite属性
SameSite Cookie 允许服务器要求某个 cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
SameSite 可以有下面三种值:
None。浏览器会在同站请求、跨站请求下继续发送 cookies,不区分大小写。
Strict。浏览器将只在访问相同站点时发送 cookie。
Lax。与 Strict 类似,但用户从外部站点导航至 URL 时(例如通过链接)除外。 在新版本浏览器中,为默认选项,Same-site cookies 将会为一些跨站子请求保留,如图片加载或者 frames 的调用,但只有当用户从外部站点导航到 URL 时才会发送。如 link 链接
2.6 安全问题
- 会话劫持和XSS
- 跨站请求伪造(CSRF)
2.7 开发中可能遇见的坑
[笔者踩过的坑-cookie离谱的生效范围]#(../README.md#十遇见的bug)
3. session
3.1 介绍
于cookie不同的是,session是存储在服务器上的,但是任然需要客户端有一个凭证,才可以在服务器上找到对应的session,这个凭证我们一般叫做sessionId。
关于sessionId的操作一般有两种,一种是直接写在cookie中,这种对于用户和前端开发的小伙伴是无感知的,理由是cookie会自动将sessionId发给服务器;另一种是前端的小伙伴重写URL,将sessionId写入到URL中,这种方式对于前端开发的小伙伴来说都是能感知到的,一般在禁用cookie的浏览器很实用。
3.2 使用过程中需要注意的事情
【bug-001】负载均衡导致会话异常(ps:关于负载均衡技术这里不展开)
当您的系统已经庞大到使用负载均衡技术时,可能就会出现用户会话异常的问题。A用户刚刚登录了,正常访问了几个页面之后突然掉线了,刷新了几下还是掉线的,当A这个时候准备去登录的时候,刷新发现自己又是在线的。对于这种“怪异”行为,就是服务器端的session没有被用户的sessionId查找到,从而导致的异常问题。
【fix-bug-001】两种方式
(1) 方式一:调整Nginx的负载均衡方案
例如原先可能使用的是轮询方式,改为ip_hash方案即可将用户的请求打在一台服务器上,这样就不会出现去别的服务器上找session的问题。
【好处】: 服务器代码几乎不用改动
【坏处】:要是这台服务器挂掉了,那么就没办法[将当前用户]实时切换到正常的节点上,也就是出现所谓的“黑户”,就是A、B两个人访问同一个系统,结果A怎么刷新都访问异常,B却可以正常操作
(2) 方式二:调整服务器端关于session存储的位置
这种方式稍微复杂点,需要修改后台服务器代码,将原先存储的session的服务器集中起来,及所有后端服务器都从一个地方取用户的session,这样就可以避免单台服务器找不到session的问题。
【好处】:相对于方式一,单台服务器的异常不会影响用户的使用体验。
【坏处】:需要修改后台服务器的代码;对于存储session的服务器也需要做高可用处理,否则崩掉之后所有用户都无法正常使用。
4. token
4.1 介绍
token解释为令牌,简而言之就是在用户登录之后,给用户颁发一个可识别身份的令牌,而这个令牌不需要存储在服务器端。这里需要注意的是token在使用过程中需要加密,避免被有心之人篡改。
4.2 常见用法
在介绍常见用法之前,说一个简单的思路:用户登录之后,将用户ID当做token返回给客户端,客户端后面每次请求带上这个ID,那么我就知道你是谁了。注意!!!刚刚说过,token在使用过程中一定要加密,否则像刚刚的操作,要是被有心之人反推生成过程,那么他就可以偷偷伪造别人的token来请求网站,从而达到窃取用户数据的操作。
那么怎么加密呢,当前最常见的做法就是使用JSON Web Token (JWT)来生成我们所说的token,这样就可以避免被人伪造了。
看看JWT的生成方式:JWT由三部分组成,它们之间用圆点(.)连接。分别是Header, Payload, Signature。
【Header】:由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。
【Payload】:包含声明。声明是关于实体和其他数据的声明,理论上你想写啥都可以。官方写法
iss:Issuer,发行者
sub:Subject,主题
aud:Audience,观众
exp:Expiration time,过期时间
nbf:Not before
iat:Issued at,发行时间
jti:JWT ID
【Signature】:将Header、Payload分别使用base64进行encoding后使用Header中的算法进行签名HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
5. 小结
综上,他们在各处都能发挥自己的力量,了解并掌握他们的特性,这样才能在日常开发过程中游刃有余。