文章内容简介:
- cookie
- session
- token
- jwt
- 单点登录
- localstorage sessionstorage
- 总结
HTTP是无状态的,但有时候需要维护用户的状态,这里就需要用到前端存储,下面会对几种常见的方式进行介绍。
1. cookie
1.1 cookie的定义 HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
1.2 cookie的分类和存储方式 cookie保存的位置是在客户端。 分为内存cookie和硬盘cookie,内存Cookie由浏览器维护,保存在内存中,浏览器关闭后就消失了,其存在时间是短暂的;硬盘Cookie保存在硬盘里,有一个过期时间,除非用户手工清理或到了过期时间,硬盘Cookie不会被删除,其存在时间是长期的。
1.3 cookie的作用 通常Cookie用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的HTTP协议记录稳定的状态信息成为了可能。
Cookie 主要用于以下三个方面: 会话状态管理(如用户登录状态、购物车等其它需要记录的信息) 个性化设置(如用户自定义设置、主题等) 浏览器行为跟踪(如跟踪分析用户行为等)
1.4 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属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 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属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。另一方面,如果当前协议是HTTP,浏览器会自动忽略服务器发来的Secure属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS协议,该开关自动打开。 HttpOnly属性指定该 Cookie 无法通过 JavaScript脚本拿到,主要是Document.cookie属性、XMLHttpRequest对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie。
1.5 服务端创建cookie 当服务器收到 HTTP 请求时,服务器可以在响应头里面添加一个 Set-Cookie 选项。 浏览器收到响应后通常会保存下 Cookie,之后对该服务器每一次请求中都通过 Cookie 请求头部将 Cookie 信息发送给服务器。另外,Cookie 的过期时间、域、路径、有效期、适用站点都可以根据需要来指定。
Set-Cookie: <cookie名>=<cookie值>
1.6 存在的问题 问题1 安全问题 明文传输 在浏览器发送cookie给客户端的过程中,有可能会被劫持,被用于非法行为,导致用户的会话收到攻击。 问题2 安全问题 潜在网络攻击 跨站请求伪造(CSRF),是一种挟持用户在当前已登录的Web应用程序上执行非本意操作的攻击方法。
跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。 由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
不过这种情况有很多解决方法,特别对于银行这类金融性质的站点,用户的任何敏感操作都需要确认,并且敏感信息的 Cookie 只能拥有较短的生命周期。 问题3 网络负载搞 问题4 数量和容量限制 Cookie有容量和数量的限制,每次都要发送很多信息带来额外的流量消耗、复杂的行为Cookie无法满足要求。
2.Session
2.1 存储方式 与cookie的对比 Cookie机制在最初和服务端完成交互后,保持状态所需的信息都将存储在客户端,后续直接读取发送给服务端进行交互。Session代表服务器与浏览器的一次会话过程,并且完全由服务端掌控,实现分配ID、会话信息存储、会话检索等功能。 2.2 session运行机制
Session机制将用户的所有活动信息、上下文信息、登录信息等都存储在服务端,只是生成一个唯一标识ID发送给客户端,后续的交互将没有重复的用户信息传输,取而代之的是唯一标识ID。
当客户端第一次请求session对象时候,服务器会为客户端创建一个session,并将通过特殊算法算出一个session的ID,用来标识该session对象。 当浏览器下次请求别的资源的时候,浏览器会将sessionID放置到请求头中,服务器接收到请求后解析得到sessionID,服务器找到该id的session来确定请求方的身份和一些上下文信息。
- 浏览器登录发送账号密码,服务端查用户库,校验用户
- 服务端把用户登录状态存为 Session,生成一个 sessionId
- 通过登录接口返回,把 sessionId set 到 cookie 上
- 此后浏览器再请求业务接口,sessionId 随 cookie带上
- 服务端查 sessionId 校验 session
- 成功后正常做业务处理,返回结果
2.3 session的实现方式 session的实现主要两种方式:cookie与url重写,而cookie是首选方式,因为各种现代浏览器都默认开通cookie功能,但是每种浏览器也都有允许cookie失效的设置,因此对于Session机制来说还需要一个备胎。
Session和Cookie没有直接的关系,可以认为Cookie只是实现Session机制的一种方法途径而已,没有Cookie还可以用别的方法。
2.4 session存在的问题 问题1 存储压力 由于Session信息是存储在服务端的,因此如果用户量很大的场景,Session信息占用的空间就不容忽视。 问题2 分布式系统中信息的共享问题 对于大型网站必然是集群化&分布式的服务器配置,如果Session信息是存储在本地的,那么由于负载均衡的作用,原来请求机器A并且存储了Session信息,下一次请求可能到了机器B,此时机器B上并没有Session信息。
3. Token
Token是令牌的意思,由服务端生成并发放给客户端,是一种具有时效性的验证身份的手段。 Token避免了Session机制带来的海量信息存储问题,也避免了Cookie机制的一些安全性问题,在现代移动互联网场景、跨域访问等场景有广泛的用途。
3.1 Token的机制
- 客户端将用户的账号和密码提交给服务器
- 服务器对其进行校验,通过则生成一个token值返回给客户端,作为后续的请求交互身份令牌
- 客户端拿到服务端返回的token值后,可将其保存在本地,以后每次请求服务器时都携带该token,提交给服务器进行身份校验
- 服务器接收到请求后,解析关键信息,再根据相同的加密算法、密钥、用户参数生成sign与客户端的sign进行对比,一致则通过,否则拒绝服务
- 验证通过之后,服务端就可以根据该Token中的uid获取对应的用户信息,进行业务请求的响应
3.2 Token的组成内容
- Header头部信息
记录了使用的加密算法信息 - Payload 净荷信息
记录了用户信息和过期时间等 - Signature 签名信息
根据header中的加密算法和payload中的用户信息以及密钥key来生成,是服务端验证服务端的重要依据
header和payload的信息不做加密,只做一般的base64编码,用于防篡改,服务端收到token后剥离出header和payload获取算法、用户、过期时间等信息,然后根据自己的加密密钥来生成sign,并与客户端传来的sign进行一致性对比,来确定客户端的身份合法性。
这样就实现了用CPU加解密的时间换取存储空间,同时服务端密钥的重要性就显而易见,一旦泄露整个机制就崩塌了,这个时候就需要考虑HTTPS了。
3.3 token特点
- Token可以跨站共享,实现单点登录
- Token机制无需太多存储空间,Token包含了用户的信息,只需在客户端存储状态信息即可,对于服务端的扩展性很好
- Token机制的安全性依赖于服务端加密算法和密钥的安全性
4. jwt
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
4.1 jwt的构成
-
Header头部信息 jwt的头部承载两部分信息: 声明类型,这里是jwt 声明加密的算法 通常直接使用 HMAC SHA256 然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分. -
Payload 净荷信息 标准中注册的声明 公共的声明 私有的声明 -
Signature 签名信息 这个签证信息由三部分组成: header (base64后的) payload (base64后的) secret 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行secret组合加密,然后就构成了jwt的第三部分。
在这里插入图片描述
注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt了。
4.2 jwt与token的对比 token需要查库验证token 是否有效,而JWT不用查库或者少查库,直接在服务端进行校验,并且不用查库。因为用户的信息及加密信息在第二部分payload和第三部分签证中已经生成,只要在服务端进行校验就行,并且校验也是JWT自己实现的。
4.3 jwt需要注意的点
- 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
- 保护好secret私钥,该私钥非常重要。
- 如果可以,请使用https协议
5. 单点登录
前面我们已经知道了,在同域下的客户端/服务端认证系统中,通过客户端携带凭证,维持一段时间内的登录状态。但当我们业务线越来越多,就会有更多业务系统分散到不同域名下,就需要「一次登录,全线通用」的能力,叫做「单点登录」。
方法一:虚拟(主域名相同) 简单的,如果业务系统都在同一主域名下,比如wenku.baidu.com tieba.baidu.com,就好办了。可以直接把 cookie domain 设置为主域名 baidu.com。
方法二:真实(主域名不同) 这要能实现「一次登录,全线通用」,才是真正的单点登录。这种场景下,我们需要独立的认证服务,通常被称为 SSO。「一次「从 A 系统引发登录,到 B 系统不用登录」的完整流程」 在app中
- 用户进入 A 系统,没有登录凭证(ticket),A 系统给他跳到 SSO
- SSO 没登录过,也就没有 sso系统下没有凭证(注意这个和前面 A ticket 是两回事),输入账号密码登录
- SSO 账号密码验证成功,通过接口返回做两件事:一是种下sso 系统下凭证(记录用户在 SSO 登录状态);二是下发一个 ticket
- 客户端拿到 ticket,保存起来,带着请求系统 A 接口
- 系统 A 校验 ticket,成功后正常处理业务请求
- 此时用户第一次进入系统 B,没有登录凭证(ticket),B 系统给他跳到 SSO
- SSO 登录过,系统下有凭证,不用再次登录,只需要下发 ticket
- 客户端拿到 ticket,保存起来,带着请求系统 B 接口
在浏览器中:
-
在 SSO 域下,SSO 不是通过接口把 ticket 直接返回,而是通过一个带 code 的 URL 重定向到系统 A 的接口上,这个接口通常在 A 向 SSO 注册时约定 -
浏览器被重定向到 A 域下,带着 code 访问了 A 的 callback接口,callback 接口通过 code 换取 ticket -
这个 code 不同于 ticket,code 是一次性的,暴露在 URL中,只为了传一下换 ticket,换完就失效 -
callback 接口拿到 ticket 后,在自己的域下 set cookie 成功 -
在后续请求中,只需要把 cookie 中的 ticket 解析出来,去 SSO 验证就好 -
访问 B 系统也是一样
6. localStorage sessionStorage
6.1 定义 存储位置 localStorage和sessionStorage一样都是用来存储客户端临时信息的对象。 他们均只能存储字符串类型的对象(虽然规范中可以存储其他原生类型的对象,但是目前为止没有浏览器对其进行实现。 localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。 sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。 不同浏览器无法共享localStorage或sessionStorage中的信息。相同浏览器的不同页面间可以共享相同的 localStorage(页面属于相同域名和端口),但是不同页面或标签页间无法共享sessionStorage的信息。这里需要注意的是,页面及标 签页仅指顶级窗口,如果一个标签页包含多个iframe标签且他们属于同源页面,那么他们之间是可以共享sessionStorage的。 6.2 风险 同样也会有xss攻击的风险 6.3 使用范围 因为考虑到每个 HTTP 请求都会带着 Cookie 的信息,所以 Cookie 当然是能精简就精简啦,比较常用的一个应用场景就是判断用户是否登录。针对登录过的用户,服务器端会在他登录时往 Cookie 中插入一段加密过的唯一辨识单一用户的辨识码,下次只要读取这个值就可以判断当前用户是否登录啦。曾经还使用 Cookie 来保存用户在电商网站的购物车信息,如今有了 localStorage,似乎在这个方面也可以给 Cookie 放个假了~
而另一方面 localStorage 接替了 Cookie 管理购物车的工作,同时也能胜任其他一些工作。比如HTML5游戏通常会产生一些本地数据,localStorage 也是非常适用的。如果遇到一些内容特别多的表单,为了优化用户体验,我们可能要把表单页面拆分成多个子页面,然后按步骤引导用户填写。这时候 sessionStorage 的作用就发挥出来了。
7.总结
- HTTP 是无状态的,为了维持前后请求,需要前端存储标记
- cookie 是一种完善的标记方式,通过 HTTP 头或 js操作,有对应的安全策略,是大多数状态管理方案的基石
- session 是一种状态管理方案,前端通过 cookie 存储 id,后端存储数据,但后端要处理分布式问题 t
- token 是另一种状态管理方案,相比于 session不需要后端存储,数据全部存在前端,解放后端,释放灵活性 token 的编码技术,通常基于 base64,或增加加密算法防篡改,jwt是一种成熟的编码方案
- 在复杂系统中,token 可通过 service token、refresh token的分权,同时满足安全性和用户体验
- session 和 token 的对比就是「用不用cookie」和「后端存不存」的对比
- 单点登录要求不同域下的系统「一次登录,全线通用」,通常由独立的 SSO 系统记录登录状态、下发 ticket,各业务系统配合存储和认证ticket
参考文章: https://mp.weixin.qq.com/s/ZAT9ImeyQwnS4i67PwIlLg https://mp.weixin.qq.com/s?__biz=MzIyMDkwODczNw==&mid=2247501360&idx=2&sn=c64e3ad0353a9a3761539e98ddaad402&chksm=97c65b9ea0b1d28897573dff4245810102ebafc1083ce3317452be993fb432dda03102caa03e&scene=132#wechat_redirect https://www.jianshu.com/p/576dbf44b2ae https://www.cnblogs.com/chentao17180/p/14514494.html https://blog.csdn.net/justlpf/article/details/82662365
|