HTTP 是一个无状态协议。这样做好处就是请求快速,但是各个网页之间无法通信。当你在某个网站上登录后,该网站的其它页面无法得知该消息。使得每次只能重新登录,用户体验很不好。本文就来盘点各种保持登录状态的认证方式

Session-Cookie

Session 和 Cookie 结合是最早的登录认证方式,它们使得同一域名下的所有网页可以互相通信。
基于 Session-Cookie 的客户端与服务器之间的登录流程:
登录认证方式 - 图1

  1. 客户端发送请求,并携带用户名和密码。
  2. 服务器接收到请求后,验证用户名、密码是否正确。通过后,将用户信息存入 Session 中,向客户端发送响应,并携带 Set-Cookie 响应头,其中包含传递给客户端的 SessionID 信息。
  3. 客户端接收到响应后,将 Set-Cookie 中的信息取出来放到 Cookie 中。下次发送请求自动在请求头中携带 Cookie
  4. 服务端收到请求后,取出 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 的客户端与服务器之间的登录流程:
  1. 客户端发送请求,并携带用户名和密码。
  2. 服务器接收到请求后,验证用户名、密码是否正确。通过后,生成 token,通过响应发送给客户端。
  3. 客户端接收到响应后,token 保存到本地(WebStorage 或 IndexedDB)。下一次请求时将 token 指定为 Authorize:Breare token header 头发送给服务端。(也可以是其它方式:如将 token 放到 uri 中)
  4. 服务端收到请求后,取出 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 来举例子):
  1. 用户访问目标网站,选择 Github 登录。
  2. 访问 Github OAuth /authorize 接口。
  3. Github 回调 /redirect-url 并携带 code。
  4. 访问 Github /access_token 接口,并传入刚刚获取到的 code。
  5. Github 回调 /redirect-url 并携带 access_token。
  6. 访问 Github /user 接口,并传入刚刚获取到的 access_token。
  7. Github 返回 User 信息。
  8. 拿到 User 信息后,存入数据库,更新登录状态(Session-Cookie 或 JWT)方式。
  9. 响应用户,已经登录成功。

可以看到 OAuth 只是一个登录授权过程。最终的结果是获取到 access_token 从而从 Github 中获取到用户信息。
当然有的 OAuth 是获取到 token 值(这样自己就可以不用维护登录态),从而实现登录。