从HTTP是无状态说起,因为 HTTP 请求方和响应方间无法维护状态,都是一次性的,它不知道前后的请求都发生了什么。但在有些应用场景下,我们需要维护用户状态,比如登录状态,最典型的,一个用户登陆微博,发布、关注、评论,都应是在登录后的用户状态下的。
一、“前端”标记方式:Cookie
Cookie是服务端发送到用户浏览器并且保存到本地的一小块数据,它会在浏览器下次向同一服务器发起请求时,被携带到服务器上。
- 经常用来做一些用户会话状态管理、个性化设置等等。
- cookie是跨域的,也就是在不同的域名中,访问的cookie的时候,只能访问对应的域名的cookie。
CSRF(跨站伪造请求)
用户登陆了a网站(后续所有请求都携带a网站的Cookies),然后跳转到b网站,b网站直接发送一个a网站的请求(b网站发送带a网站Cookies的请求,因此称为“伪造”),进行一些危险操作,就发生了CSRF攻击!
- 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。
- 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
最好的办法是使用Token。一般情况下我们使用 JWT 的话,在我们登录成功获得 JWT 之后,一般会选择存放在 localStorage 中。前端的每一个请求后续都会附带上这个 JWT,整个过程压根不会涉及到 Cookie。因此,即使你点击了非法链接发送了请求到服务端,这个非法请求也是不会携带 JWT 的,所以这个请求将是非法的。
XSS(跨站脚本攻击)
XSS是由于不安全的数据引起的,有可能是表单提交的数据,有可能是页面路径的参数问题。
- XSS是通过盗取用户的敏感信息而达到攻击的目的
常见的避免 XSS 攻击的方式是过滤掉请求中存在 XSS 攻击风险的可疑字符串。
在 Spring 项目中,我们一般是通过创建 XSS 过滤器来实现的。
二、“前后端”状态管理方案:Session
Session 代表着服务器和客户端一次会话的过程。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。
cookie是实现session认证的一种途径
Session 的分布式问题
通常服务端是集群,而用户请求过来会走一次负载均衡,不一定打到哪台机器上。那一旦用户后续接口请求到的机器和他登录请求的机器不一致,或者登录请求的机器宕机了,session 不就失效了吗?
这个问题现在有几种解决方式。
- 一是从「存储」角度,把 session 集中存储。如果我们用独立的 Redis 或普通数据库,就可以把 session 都存到一个库里。
- 二是从「分布」角度,让相同 IP 的请求在负载均衡时都打到同一台机器上。以 nginx 为例,可以配置 ip_hash 来实现。
但通常还是采用第一种方式,因为第二种相当于阉割了负载均衡,且仍没有解决「用户请求的机器宕机」的问题。
三、“前端”状态管理方案:token
cookie 并不是客户端存储凭证的唯一方式。token 因为它的「无状态性」,有效期、使用限制都包在 token 内容里,对 cookie 的管理能力依赖较小,客户端存起来就显得更自由。但 web 应用的主流方式仍是放在 cookie 里,毕竟少操心。
token 的流程是这样的:
- 用户登录,服务端校验账号密码,获得用户信息
- 把用户信息、token 配置编码成 token,通过 cookie set 到浏览器
- 此后用户请求业务接口,通过 cookie 携带 token
- 接口校验 token 有效性,进行正常业务接口处理
总结
狭义上,我们通常认为 session 是「种在 cookie 上、数据存在服务端」的认证方案,token 是「客户端存哪都行、数据存在 token 里」的认证方案。对 session 和 token 的对比本质上是「客户端存 cookie / 存别地儿」、「服务端存数据 / 不存数据」的对比。
客户端存 cookie / 存别地儿
存 cookie 固然方便不操心,但问题也很明显:
- 在浏览器端,可以用 cookie(实际上 token 就常用 cookie),但出了浏览器端,没有 cookie 怎么办?
- cookie 是浏览器在域下自动携带的,这就容易引发 CSRF 攻击
存别的地方,可以解决没有 cookie 的场景;通过参数等方式手动带,可以避免 CSRF 攻击。
服务端存数据 / 不存数据
- 存数据:请求只需携带 id,可以大幅缩短认证字符串长度,减小请求体积
- 不存数据:不需要服务端整套的解决方案和分布式处理,降低硬件成本;避免查库带来的验证延迟
四、token 生成方案:JWT(JSON Web Token)
JSON Web Token (JWT) 是一个开放标准,定义了一种传递 JSON 信息的方式。这些信息通过数字签名确保可信。
JWT 本质上就是一组字串,通过(.
)切分成三个为 Base64 编码的部分:
- Header(头部) : 描述 JWT 的元数据,定义了生成签名的算法以及
Token
的类型。Header 被 Base64Url 编码后成为 JWT 的第一部分。 - Payload(载荷) : 用来存放实际需要传递的数据,包含声明(Claims),如
sub
(subject,主题)、jti
(JWT ID)。Payload 被 Base64Url 编码后成为 JWT 的第二部分。Payload 部分默认是不加密的,一定不要将隐私信息存放在 Payload 当中!!! - Signature(签名):服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。生成的签名会成为 JWT 的第三部分。JWT 安全的核心在于签名,签名安全的核心在密钥。
两点建议:
- 建议将 JWT 存放在 localStorage 中,放在 Cookie 中会有 CSRF 风险。
- 请求服务端并携带 JWT 的常见做法是将其放在 HTTP Header 的
Authorization
字段中(Authorization: Bearer Token
)。
如何加强 JWT 的安全性?
- 使用安全系数高的加密算法。
- 使用成熟的开源库,没必要造轮子。
- JWT 存放在 localStorage 中而不是 Cookie 中,避免 CSRF 风险。
- 一定不要将隐私信息存放在 Payload 当中。
- 密钥一定保管好,一定不要泄露出去。JWT 安全的核心在于签名,签名安全的核心在密钥。
- Payload 要加入
exp
(JWT 的过期时间),永久有效的 JWT 不合理。并且,JWT 的过期时间不易过长。
五、单点登录——「一次登录,全线通用」
-
“虚假”的单点登录(主域名相同):如果业务系统都在同一主域名下,比如
wenku.baidu.com
tieba.baidu.com
,就好办了。可以直接把 cookie domain 设置为主域名baidu.com
,百度也就是这么干的。 -
“真实”的单点登录(主域名不同):比如滴滴这么潮的公司,同时拥有
didichuxing.com
xiaojukeji.com
didiglobal.com
等域名,种 cookie 是完全绕不开的。这要能实现「一次登录,全线通用」,才是真正的单点登录。这种场景下,我们需要独立的认证服务,通常被称为 SSO。
- 用户进入 A 系统,没有登录凭证(ticket),A 系统给他跳到 SSO
- SSO 没登录过,也就没有 sso 系统下没有凭证(注意这个和前面 A ticket 是两回事),输入账号密码登录
- SSO 账号密码验证成功,通过接口返回做两件事:一是种下 sso 系统下凭证(记录用户在 SSO 登录状态);二是下发一个 ticket
- 客户端拿到 ticket,保存起来,带着请求系统 A 接口
- 系统 A 校验 ticket,成功后正常处理业务请求
- 此时用户第一次进入系统 B,没有登录凭证(ticket),B 系统给他跳到 SSO
- SSO 登录过,系统下有凭证,不用再次登录,只需要下发 ticket
- 客户端拿到 ticket,保存起来,带着请求系统 B 接口
六、参考
你真的了解 Cookie 和 Session 吗 - 掘金 (juejin.cn)
Cookie的设置、读取以及是否自动携带问题 - 掘金 (juejin.cn)
前后端数据交互(六)——ajax 、fetch 和 axios 优缺点及比较 - 掘金 (juejin.cn)
面试:彻底理解Cookie以及Cookie安全 - 掘金 (juejin.cn)
前端鉴权的兄弟们:cookie、session、token、jwt、单点登录 - 掘金 (juejin.cn)
HTTP协议之涉及到cookie字段 - 掘金 (juejin.cn)
/juejin.cn/post/6844904102544031757)
前端鉴权的兄弟们:cookie、session、token、jwt、单点登录 - 掘金 (juejin.cn)
HTTP协议之涉及到cookie字段 - 掘金 (juejin.cn)
原文链接:https://blog.csdn.net/klji234/article/details/124314796