HTTP 是一个无状态协议。这样做好处就是请求快速,但是各个网页之间无法通信。当你在某个网站上登录后,该网站的其它页面无法得知该消息。使得每次只能重新登录,用户体验很不好。本文就来盘点各种保持登录状态的认证方式
Session-Cookie
Session 和 Cookie 结合是最早的登录认证方式,它们使得同一域名下的所有网页可以互相通信。
基于 Session-Cookie 的客户端与服务器之间的登录流程:
- 客户端发送请求,并携带用户名和密码。
- 服务器接收到请求后,验证用户名、密码是否正确。通过后,将用户信息存入
Session
中,向客户端发送响应,并携带Set-Cookie
响应头,其中包含传递给客户端的SessionID
信息。 - 客户端接收到响应后,将
Set-Cookie
中的信息取出来放到Cookie
中。下次发送请求自动在请求头中携带Cookie
。 - 服务端收到请求后,取出
Cookie
中的SessionID
,在Session
中核对信息,成功后向客户端发送响应。
上述过程简单来讲就是在客户端存入 Cookie
,服务端存入 Session
。在认证的过程中,客户端发送 Cookie
给服务端,服务端拿到后与 Session
做一个验证。通过则说明已登录。
这种方式很好的解决了用户登录状态维持的难题。而且在客户端只用在 Cookie
从存入 SessionID
的值,在发送过程中也更轻便。
Cookie 缺点
- 由于每次发送请求,
Cookie
会自动添加在请求头中被发送出去。这样就可能造成了 CSRF 攻击。 - 有些客户端不支持
Cookie
。比如在移动端的为了减少额外的性能开销而取消Cookie
。 Cookie
自身的限制。单个 Cookie 的大小为 4 KB,单个站点最多 20 个Cookie
。混合嵌套开发的问题。小程序跳转 H5 页面,不能携带
Cookie
。Session 缺点
由于
Session
只保存在服务器中,在分布式中共享Session
的问题。在分布式中Session
的解决可以看这篇文章。Session
中保存着用户的信息,当登录用户过多对服务器造成的较大压力。Token
与传统的 Session-Cookie 认证方式不同,Token 是一种无状态的认证方式。它是将非必要的用户信息保存到 token 中,服务器每次只用检查 token 中的信息来判断用户。
JWT 是基于 token 的一种实现。它的全称为 Json Web Token。具体的原理与细节可以看 JWT 官方的介绍。
基于 JWT 的客户端与服务器之间的登录流程:
- 客户端发送请求,并携带用户名和密码。
- 服务器接收到请求后,验证用户名、密码是否正确。通过后,生成 token,通过响应发送给客户端。
- 客户端接收到响应后,token 保存到本地(WebStorage 或 IndexedDB)。下一次请求时将 token 指定为
Authorize:Breare token
header 头发送给服务端。(也可以是其它方式:如将 token 放到 uri 中) - 服务端收到请求后,取出 token,拿到其中的信息。验证 token 中的用户信息,符合条件就返回响应。
与 Session-Cookie 最大的不同就是服务器端没有保存用户信息了。认证信息都保存在 token 中。另外,由于 token 不受服务器管控,应该将 token 的有效期设置的短一些。
Token 优点
- 无状态。
- 相较于 Session,Token 的数据量极小。
在 JWT 中,由于 JSON 的通用性,使得各语言可以很好的支持。
Token 缺点
由于保存在本地,要防止 XSS 攻击。避免 token 被人获取从而伪造请求。
- 务必要使用 HTTPS 请求,在 token 的认证方式中 token 是唯一的认证凭证,而 HTTP 无法防止 中间人攻击 从而导致 token 不可信。
- 最大的问题在于一旦发送 token,只有在到期之后才能失效。这是因为 token 是无状态的,无法在使用过程中废除 token 或修改 token 的权限。(有人可能会说维护一个 token 表,但是这不是又回到 Session-Cookie 的老路了吗?)
OAuth 三方认证
OAuth 是一个关于授权的行业标准协议,目前的版本是2.0版。它有多种授权类型,这里重点说授权码的授权流程:
基于 OAuth 的授权流程(这里用 Github 的 OAuth 来举例子):
- 用户访问目标网站,选择 Github 登录。
- 访问 Github OAuth
/authorize
接口。 - Github 回调
/redirect-url
并携带 code。 - 访问 Github
/access_token
接口,并传入刚刚获取到的 code。 - Github 回调
/redirect-url
并携带 access_token。 - 访问 Github
/user
接口,并传入刚刚获取到的 access_token。 - Github 返回 User 信息。
- 拿到 User 信息后,存入数据库,更新登录状态(Session-Cookie 或 JWT)方式。
- 响应用户,已经登录成功。
可以看到 OAuth 只是一个登录授权过程。最终的结果是获取到 access_token 从而从 Github 中获取到用户信息。
当然有的 OAuth 是获取到 token 值(这样自己就可以不用维护登录态),从而实现登录。