常见Web鉴权方案

鉴权 & 持久化登陆

鉴权

鉴权Authentication)是指确认用户身份的过程。
在计算机系统中,鉴权是一种安全机制,用于验证用户是否具有访问系统资源的权限。通过鉴权机制,系统可以验证用户提供的凭据(如用户名和密码、数字证书等),并决定是否授予用户所请求的访问权限。

传统的鉴权是通过密码来实现的。这种方式的前提是,每个获得密码的用户都已经被授权。在建立用户时,就为此用户分配一个密码,用户的密码可以由管理员指定,也可以由用户自行申请。

持久化登陆

持久化登录Persistent Login)是一种使用户在多次会话之间保持登录状态的机制。
一般情况下,每个 HTTP 请求都是独立的,服务器不会保存关于客户端的信息,因此服务器无法识两个连续请求是否来自相同用户。这被称为 HTTP 协议的无状态性

无状态性对于 Web 的可伸缩性和简单性至关重要。服务器不需要维护大量的客户端状态信息,这允许服务器更轻松地处理大量并发请求

为了解决无状态性带来的问题,持久化登录机制应运而生。通常情况下,用户在登录后,系统会为其分配一个会话标识,并将该标识存储在客户端。这样,在用户下次访问网站时,系统可以通过检查客户端中的会话标识来自动识别用户,若验证成功,则将其视为已登录状态,而无需用户再次输入用户名和密码。

HTTP Basic Authentication

HTTP 提供一个用于权限控制和认证的通用框架,HTTP Basic Authentication 是最常用的由 HTTP 协议定义的认证方式。

常见的 Basic HTTP 认证 实现流程如下:

  1. 当客户端请求一个需要认证的资源时,服务器会返回一个 401 状态码,并在响应头中添加一个 WWW-Authenticate 字段,指明认证的方式(Basic)和安全域(realm)。
  2. 客户端收到 401 响应后,会弹出一个对话框,让用户输入用户名和密码。用户输入对应内容,然后客户端会将用户名和密码用冒号连接,使用 Base64 编码,再加上 Basic 前缀,作为 Authorization 字段的值,发送给服务器。
  3. 服务器收到 Authorization 字段后,会解码得到用户名和密码,然后进行验证。如果验证通过,服务器会返回请求的资源。如果验证失败,服务器会再次返回 401 响应。

除此之外,还有 Bearer、Digest 等 HTTP 认证方案。如果你感兴趣,请移步此网址进行查看:Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry

如果你想给自己的网站添加鉴权功能,同时又不想编写一套前后端鉴权系统,可以尝试 HTTP Basic Authentication,它足以提供基本的鉴权功能,并且足够简单,简单到你只需要在 Nginx 中进行几句配置。

使用 Nginx 为你的网站添加 HTTP 身份认证,可以参考这篇文章:[Nginx Http基本身份认证](HTTP Basic Authentication 足以提供一个基本的鉴权功能,简单易用,)。

于此同时,HTTP Basic Authentication 的缺点也很明显:用户名和密码都是明文传输,容易被窃听或者重放,一般需要配合 HTTPS 来保证传输的安全性;不支持注销操作,只能关闭浏览器清除认证信息;只能为网站提供保护,很难实现私有路由;只能使用浏览器自带的提示框,不能自定义登陆页面……

可以使用 session-cookie 实现前后端的鉴权认证。

CookieHTTP Cookie)是一些 字符串数据,存储在客户端。

服务器可以通过 HTTP 响应头将一个或多个 cookie 发送给客户端,客户端的浏览器会将这些 cookie 存储在本地。每次客户端向服务器发送请求时,浏览器会自动将与请求相关的 cookie 附加到请求头中发送给服务器。

session

Session会话)是服务器为了保存用户状态而创建的一个特殊的 对象

当浏览器第一次访问服务器时,服务器创建一个 session 对象 (该对象有一个唯一的 id ,一般称之为 SessionId),服务器会将 SessionId 以 cookie 的方式发送给浏览器。当浏览器再次访问服务器时,会将 SessionId发送过来,服务器依据 SessionId 就可以找到对应的 session 对象,并对用户身份进行验证。

实现流程

  1. 用户首次访问网站,服务器会创建一个 Session,并生成一个与之对应的 sid,创建的 Session 一般保存在服务器 内存数据库 或者 redis 中,通常是 redis;
  2. 服务器将 sid 填入 Cookie 中,然后将 Cookie 发送给客户端,客户端在接受到 Cookie 后将其保存在本地,通常存在 localStorage 或者 localCookie 中;
  3. 客户端在后续的请求中会携带这个 Cookie,服务器通过 Cookie 中的 sid 找到对应的 Session,从而识别用户状态,判断请求是否合法;
  4. 当用户退出登录或者 Session 过期后,服务器会销毁对应的 Session。

你也可以参考下面这张图,更方便理解:

session实现流程

其他

session 和 cookie 经常同时使用,它们之间存在着很多区别,这里不做详细介绍,可阅读这篇文章:Cookie和Session的区别

当然,session-cookie 鉴权方案也存在一些不足:后端服务需要大量空间来存储用户身份信息;依赖 cookie 实现,无法在禁用 cookie 的浏览器中使用,同时容易出现 CSRF 漏洞;对移动端的支持不完善……

JWT

JSON Web TokenJWT)是为在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准,一般被用来在服务端和客户端之间传递,以验证用户身份信息。

JWT 是一个字符串,它由三部分组成:头部、载荷与签名,不同部分之间用 . 分隔。如果你对 JWT 的结构感兴趣,可以看看:jwt.io,这个网站提供了对 JWT 的详细介绍以及在线编码、解码 jwt 的功能。

使用 jwt 实现鉴权的一般流程如下:

  1. 用户登认证
    用户向服务器提交身份凭证,例如用户名和密码。
  2. 验证身份生成 jwt
    服务器验证用户提交的身份凭证的有效性,如果身份凭证有效,服务器会生成一个jwt,然后携带 jwt 返回给客户端。
  3. 客户端存储 jwt
    客户端接受到 jwt 后,将其保存在本地,之后的每次相关请求都会携带。
  4. 服务器验证 jwt
    服务器从请求头中获取 token,然后解析出用户信息和声明信息,并根据这些信息来验证用户是否有权访问特定资源。

相比传统的 session-cookie 鉴权方案,JWT 鉴权有相当多的优势:

  • JWT 本身是无状态的,这意味着服务器不需要保存用户状态信息,节省了存储空间;
  • JWT 本身携带了部分用户信息,一定程度上可减轻查询数据库的需求;
  • JWT 不依赖于 Cookie,可以在禁用 cookie 的环境中运行,同时一定程度防止 CSRF 攻击;
  • JWT 体积小,传输速度快,
  • ……

OAuth

OAuth 是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。
这是一种很常见的鉴权方案,比如 GitHub 认证登陆。

如果想学习 OAuth2.0,参考这篇文章:理解 OAuth 2.0

SSO

单点登录(SSO)是一种身份验证服务,用户只需要登录一次,就可以访问所有相互信任的应用系统。一般运作流程如下:

  1. 用户首次访问系统
    当用户首次访问一个需要登录的应用时,该应用会发现用户未登录,然后将用户重定向到 SSO 认证中心,并将自己的地址作为参数。
  2. 用户在 SSO 认证中心进行登录
    用户在 SSO 认证中心输入用户名和密码进行登录。如果登录成功,服务器会生成一个 ticket,并将该 ticket 追加到原始请求的 URL 参数中。
  3. SSO系统进行认证
    SSO 系统进行认证后,将登录状态写入 SSO 的 session,并在浏览器中写入 SSO 域下的 Cookie。
  4. 生成 Service Ticket
    SSO 系统登录完成后会生成一个 ST(Service Ticket),然后跳转到原始请求的应用系统,同时将 ST 作为参数传递给该应用系统。
  5. 应用系统验证 ST
    应用系统拿到 ST 后,从后台向 SSO 发送请求,验证 ST 是否有效。如果验证通过,应用系统将登录状态写入 session 并设置 app 域下的 Cookie。
  6. 访问受保护资源
    此时用户可以访问原先请求的受保护资源。

这就是单点登录(SSO)的工作原理和实现流程。值得注意的是,实现单点登录的关键在于如何让Session ID(或Token)在多个域中共享¹。

探究 CAS 实现单点登陆的原理,参考这篇文章:一篇文章彻底弄懂CAS实现SSO单点登录原理

PassKey

一种新型的鉴权方式,摒弃了传统的账户密码的鉴权形式,实现无密码鉴权。

原理参考这篇文章:搞懂通行密钥