js|鉴权必须了解的5个知识点(cookie,session,token,jwt,单点登录)
从状态说起
[HTTP 无状态] 我们知道,HTTP是无状态的,也就是说,HTTP请求方和响应方间无法维护状态,都是一次性的,它不知道前后的请求都发生了什么
但有的场景下,我们需要维护状态,最常见的,一个用户登录微博,发布,关注,评论,都是应该在登录后的用户状态下的
[标记] 在学校或公司,入学入职那一天起,会录入你的身份、账户信息,然后给你发个卡,今后在园区内,你的门禁、打卡、消费都只需要刷这张卡
[前端存储] 这就涉及一发,一存,一带,发好办,登录接口直接返回给前端,存储就需要前端想办法了,前提是,你要把卡带在身上
前端的存储方式有很多
- 挂载到全局变量上,但这是个[体验卡],一次刷新页面就没了
- 可以存到cookie,localStorage里,这属于[会员卡],无论怎么刷新,只要浏览器没清掉或者过期,就一直拿着这个状态
可是前端好麻烦啊,又要自己存,又要想办法带出去,有没有不用操心的?有,cookie
cookie也是前端存储的一种,但相比于localStorage等其他方式,借助HTTP头,浏览器能力,cookie可以做到前端无感知
一般过程是这样的:
- 在提供标记的接口,通过 HTTP 返回头的 Set-Cookie 字段,直接「种」到浏览器上
- 浏览器发起请求时,会自动把 cookie 通过 HTTP 请求头的 Cookie 字段,带给接口
你不能拿清华的校园卡进北大cookie 是要限制:通过Domain(域) / Path(路径)两级(空间范围)
Domain属性指定浏览器发出 HTTP 请求时,哪些域名要附带这个 Cookie。如果没有指定该属性,浏览器会默认将其设为当前 URL 的一级域名,比如 www.example.com 会设为 example.com,而且以后如果访问example.com的任何子域名,HTTP 请求也会带上这个 Cookie。如果服务器在Set-Cookie字段指定的域名,不属于当前域名,浏览器会拒绝这个 Cookie。
Path属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,PATH属性是/,那么请求/docs路径也会包含该 Cookie。当然,前提是域名必须一致
[配置:Expires / Max-Age]
你毕业了卡就不好使了cookie 还可以限制 通过Expires,Max-Age中的一种 (时间范围)
Expires属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式。如果不设置该属性,或者设为null,Cookie 只在当前会话(session)有效,浏览器窗口一旦关闭,当前 Session 结束,该 Cookie 就会被删除。另外,浏览器根据本地时间,决定 Cookie 是否过期,由于本地时间是不精确的,所以没有办法保证 Cookie 一定会在服务器指定的时间过期。
Max-Age属性指定从现在开始 Cookie 存在的秒数,比如60 * 60 * 24 * 365(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。
如果同时指定了Expires和Max-Age,那么Max-Age的值将优先生效。
如果Set-Cookie字段没有指定Expires或Max-Age属性,那么这个 Cookie 就是 Session Cookie,即它只在本次对话存在,一旦用户关闭浏览器,浏览器就不会再保留这个 Cookie
[配置:Secure / HttpOnly]
有的学校规定,不带卡套不让刷(什么奇葩学校,假设);有的学校不让自己给卡贴贴纸。Secure属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的Secure属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开
HttpOnly属性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是Document.cookie属性、XMLHttpRequest对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie
[HTTP 头对cookie的读写] HTTP 返回的一个 Set-Cookie 头用于向浏览器写入「一条(且只能是一条)」cookie,格式为 cookie 键值 + 配置键值
Set-Cookie: username=jimu;
domain=jimu.com;
path=/blog;
Expires=Wed, 21 Oct 2015 07:28:00 GMT;
Secure;
HttpOnly
那我想一次多 set 几个 cookie 怎么办?多给几个 Set-Cookie 头(一次 HTTP 请求中允许重复)
[前端对cookie的读写] 前端可以自己创建 cookie,如果服务端创建的 cookie 没加HttpOnly,那恭喜你也可以修改他给的 cookie。
调用document.cookie可以创建、修改 cookie,和 HTTP 一样,一次document.cookie能且只能操作一个 cookie
document.cookie = 'username=jimu;
domain=jimu.com;
path=/blog;
Expires=Wed, 21 Oct 2015 07:28:00 GMT;
Secure;
HttpOnly';
调用document.cookie也可以读到 cookie,也和 HTTP 一样,能读到所有的非HttpOnly cookie
console.log(document.cookie);
// username=jimu;
height=180;
weight=80
那有了存储工具,接下来怎么做呢?
应用方案:服务端session 现在回想下,你刷卡的时候发生了什么?
其实你的卡上只存了一个 id(可能是你的学号),刷的时候物业系统去查你的信息、账户,再决定「这个门你能不能进」「这个鸡腿去哪个账户扣钱」这种操作,在前后端鉴权系统中,叫 session
典型的 session 登陆/验证流程:
文章图片
- 浏览器登录发送账号密码,服务端查用户库,校验用户
- 服务端把用户登录状态存为Session,生成一个sessionId
- 通过登录接口返回,把sessionId set到cookie上
- 此后浏览器再请求业务接口,sessionId 随cookie带上
- 服务端查sessionId校验session
- 成功后正常做业务处理,返回结果
Redis(推荐):内存型数据库,redis中文官方网站。以 key-value 的形式存,正合 sessionId-sessionData 的场景;且访问快。
内存:直接放到变量里。一旦服务重启就没了
数据库:普通数据库。性能不高
[Session 的过期和销毁] 很简单,只要把存储的 session 数据销毁就可以
应用方案:token
我又想到学校,在没有校园卡技术以前,我们都靠「学生证」。门卫小哥直接对照我和学生证上的脸,确认学生证有效期、年级等信息,就可以放行了回过头来想想,一个登录场景,也不必往 session 存太多东西,那为什么不直接打包到 cookie 中呢?这样服务端不用存了,每次只要核验 cookie 带的「证件」有效性就可以了,也可以携带一些轻量的信息
这种方式通常被叫做 token
文章图片
token的流程是这样的:
- 用户登录,服务端校验账号密码,获取用户信息
- 把用户信息,token配置编码成token,通过cookie set到浏览器
- 此后用户请求业务接口,通过cookie携带token
- 接口校验token有效性,进行正常业务接口处理
单点登录 前面我们已经知道了,在同域下的客户端/服务端认证系统中,通过客户端携带凭证,维持一段时间内的登录状态。
但当我们业务线越来越多,就会有更多业务系统分散到不同域名下,就需要「一次登录,全线通用」的能力,叫做「单点登录」
虚假”的单点登录(主域名相同) 简单的,如果业务系统都在同一主域名下,比如wenku.baidu.com tieba.baidu.com,就好办了。可以直接把 cookie domain 设置为主域名 baidu.com,百度也就是这么干的
“真实”的单点登录(主域名不同) 比如滴滴这么潮的公司,同时拥有didichuxing.com xiaojukeji.com didiglobal.com等域名,种 cookie 是完全绕不开的。
这要能实现「一次登录,全线通用」,才是真正的单点登录。
【js|鉴权必须了解的5个知识点(cookie,session,token,jwt,单点登录)】这种场景下,我们需要独立的认证服务,通常被称为 SSO
文章图片
- 用户进入A系统,没有登录凭证(ticket),A系统给他跳到SSO
- SSO没登录过,也就没有sso系统下没有凭证(注意这个和前面 A ticket是两回事),输入账号密码登录
- SSO账号密码验证成功,通过接口返回做两件事,一是种下sso系统下凭证(记录用户登录状态);二是下发一个ticket
- 客户端拿到ticket,保存起来,带着请求系统A接口
- 系统A检验ticket,成功后正常处理业务请求
- 此时用户第一次进入系统B,没有登录凭证(ticket),B系统给他跳到SSO
- SSO登录过,系统下有凭证,不用再次登录,只需要下发ticket
- 客户端拿到ticket,保存起来,带着请求系统B接口
- HTTP是无状态的,为了维持前后请求,需要前端存储标记
- cookie 是一种完善的标记方式,通过 HTTP 头或 js 操作,有对应的安全策略,是大多数状态管理方案的基石
- session 是一种状态管理方案,前端通过 cookie 存储 id,后端存储数据,但后端要处理分布式问题
- token 是另一种状态管理方案,相比于 session 不需要后端存储,数据全部存在前端
- token 的编码技术,通常基于 base64,或增加加密算法防篡改,jwt 是一种成熟的编码方案
- session 和 token 的对比就是「用不用cookie」和「后端存不存」的对比
- 单点登录要求不同域下的系统「一次登录,全线通用」,通常由独立的 SSO 系统记录登录状态、下发 ticket,各业务系统配合存储和认证 ticket
推荐阅读
- 我们重新了解付费。
- 拍照一年啦,如果你想了解我,那就请先看看这篇文章
- C语言中的时间函数clock()和time()你都了解吗
- 操作系统|[译]从内部了解现代浏览器(1)
- 生发知识,带你深入了解
- 了解自然大气粒子对气候的影响
- 【挑战日更】Day6.《终身学习.10个你必须掌握的未来生存法则》摘录之三
- Day10_要想看起来毫不费力,必须付出超乎常人的努力
- vue组件中为何data必须是一个函数()
- 公司注册好后,每月每季度每年必须要干的一些事