应用开发一般都少不了身份验证,而身份验证机制的稳定性对所有应用程序都变得至关重要。具体选择何种方式进行身份验证可以根据项目及团队情况来衡量,在决定之前需要先理解WEB身份验证常见的两种方式:基于 Cookie
的身份验证和基于令牌(Token
)的身份验证。
基于 Cookie 的身份验证
身份验证是将用户凭据交换为唯一身份标识的过程。
在基于
Cookie
的身份验证中,此唯一标识符 (Cookie
) 在服务器端创建并发送给浏览器。
当登录 Web 应用程序时,浏览器将从其应用程序的服务器接收一个 Cookie
,浏览器将存储它并将该 Cookie
与每个后续请求一起发送,以验证请求来自同一用户。
为了更好地理解 Cookie
的工作原理,下面将这个过程分解为 5 个部分。
Cookie 工作流程
1. 使用凭据登录到应用程序
2. 服务器验证凭据并在创建 session
服务器验证凭证成功后,创建 session
,可以存储在内存或数据库中,为了更好的扩展建议将其存储在数据库中。如果是存储在内存中,在使用负载均衡或多服务器部署的场景下会出现 session
问题。
3. 服务器通过将cookie包含在Set-Cookie 标头中来响应浏览器
这个 cookie
通过名称值对发送,它包含一个唯一的 id
来标识用户。
除此之外,Cookie
还可以包含到期日期、作用域和有效时间等详细信息。具有多个 Set-Cookie
标头的示例响应如下所示:
HTTP/2.0 200 OK Content-Type: text/html Set-Cookie: <cookie-name>=<cookie-value> Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date> Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<number> Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain> Set-Cookie: <cookie-name>=<cookie-value>; Path=<path> Set-Cookie: <cookie-name>=<cookie-value>; Secure Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly [page content]
4. 浏览器将 Cookie 存储在存储中并与后续请求一起发送
当服务器收到带有 Cookie
的请求时,它会将 Cookie
中的 session ID
与数据库中的 session
进行比较以验证用户的有效性。
可以使用浏览器开发工具在应用程序部分下的 Cookie
存储中找到浏览器中保存的所有 Cookie。
5. 当用户注销时,服务器将从数据库中删除 session
一旦用户注销,服务器将通过清除数据库 session
并使 Cookie
过期,浏览器也会从 Cookie
存储中删除它。
Cookie 特征与优缺点
上面简单介绍了一下 Cookie
的工作流程,下面来看看其特征与优缺点。
这是一个完全自动化的过程
如果使用 Cookie
进行身份验证,则无需明确开发任何内容来向请求添加 Cookie
。
浏览器会负责 Cookie 的处理,它会自动为所有请求添加 Cookie。
尽管这种自动化过程使开发变得更容易,但也有一些缺点。例如,有些请求不需要任何身份验证,但是使用这种方法,Cookie
也将在每个请求中被发送。
此外,CSRF 攻击者可以利用这种机制来欺骗浏览器向虚假网站发送带有 Cookie 的请求。
安全措施
默认情况下,基于 Cookie
的身份验证对攻击没有有效的保护,它们主要容易受到跨站脚本(XSS
)和跨站请求伪造(CSRF
)攻击。
但是,可以显式地修改 Cookie标头来保护它们免受此类攻击。
例如,在设置 Cookie
头时使用 HttpOnly
属性可以很容易地保护 Cookie
免受 XSS
攻击。
Set-Cookie: <cookie-name>=<cookie-value>; Secure Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
此外,可以在 Cookie
标头中使用 SameSite
属性来有效地防止 CSRF
攻击。
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Lax
SameSite
属性有 3 个值可用:
SameSite=Lex
:将确保浏览器不会在跨站点请求时发送Cookie
(如果没有定义 SameSite 属性,这是 Cookie 的默认行为)。SameSite=Strict
: 将确保浏览器仅针对同站点请求发送Cookie
。SameSite=Note
将允许通过跨站点和同站点请求发送Cookie
通常适用于单个域名
除非特别配置,否则 Cookie
仅适用于单个域名。尽管从表面上这似乎是一种限制,但它是默认情况下强制执行单一来源的最强大功能之一。但是,如果前端和后端 (API) 来自不同的域名或子域名,则需要在 Cookie
中将其明确列入白名单。否则,浏览器不会随请求一起发送 Cookie
。
不适合开放API
如果正在构建一个API来向客户端公开服务,那么 Cookie
可能不是最佳的选择。除非客户端只是浏览器,否则它会使客户端变得复杂。
例如,开发的是一款手机应用,与令牌 Token
相比,拥有 Cookie
将使移动应用程序 Cookie
管理变得复杂。
可能存在可扩展性问题
如前所述,服务器负责 Cookie
设置,需要在数据库中为每个用户存储 session
。
尽管有成熟的方法来处理可扩展性(例如,使用像 Redis
这样的内存数据库作为 session
的存储),但它仍然增加了更多的复杂性。
但是,随着用户数量的增加,在扩展和管理这些 session
时可能会出现问题。
最适合存储额外的数据
由于这种方法为每个用户维护单独的 session
,所以可以存储额外的数据到 session
中。
通过 Cookie
和 session
,可以存储特定的数据,如用户个性化、访问控制和 session
。然后,它允许将其用于后续请求。
然而,也可以用 Token
来实现这一点。例如,使用 JWT
令牌,可以存储 Claims
数据。然而,由于它将增加 Token
的大小,保留更多 Token
将影响更高的网络利用率。
可以限制浏览器对 Cookie 的访问
由于 Cookie
提供了 HTTP-Only
选项,可以限制 JavaScript 对它的访问。此外,它将阻止任何访问Cookie与跨站点脚本攻击。
基于令牌Token的认证
引入基于令牌的身份验证是为了解决基于 Cookie
方法的不足。
与Cookie不同,基于Token的方法需要自己实现,Token保存在客户端。
当登录到web应用程序时,服务器将验证凭据并向浏览器发送加密 Token
。然后浏览器将存储这个Token,并可以添加到后续请求的授权头中。
然而,基于 Token
方法的标准实现要比上面描述的流程复杂得多。例如,OpenID Connect
引入了多个身份验证流来处理不同类型的用例。
为了更好地理解 Token
的工作方式,下面将这个过程分解为4个部分,并以使用最广泛的 Token
标准 JWT
作为实例。
JSON Web Token (JWT) 是基于令牌的身份验证中最常用的开放标准。
Token的认证工作流程
1. 使用凭据登录到应用程序
2. 服务器验证凭据,生成令牌并使用密钥对其进行签名,然后将其发送回浏览器
通常,需要在传输时使用加密(如 SSL)来保护通道。
在服务器端,可以使用像 jsonwebtoken 这样的 NPM 库来生成这些令牌。
npm install jsonwebtoken
const jwt = require("jsonwebtoken"); const privateKey = "eyJkYXRhIjp7InVzZXJuYW1lIjoiZGV2cG9pbnQifSwiaWF0IjoxNjI3Njk3ODQ2fQ"; const token = jwt.sign( { data: { username: "devpoint", }, }, privateKey, { algorithm: "HS256" }, { expiresIn: Math.floor(Date.now() / 1000) + 60 * 60 } );
使用 jsonwebtoken 生成的 Token
如下所示:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJuYW1lIjoiZGV2cG9pbnQifSwiaWF0IjoxNjI3Njk3OTA2fQ.9v0S-74SH5UQwTAqgvNL43fAxQqW3_cajoDsum3TZEo
3. 将 Token 存储在浏览器存储中,并使用JavaScript添加到后续请求中
浏览器可以将此 Token
存储在本地存储、Session storage
或 Cookie
中。然后这个 Token
将被添加到必要请求的授权头中,并发送到服务器端进行请求验证。因此,需要使用 JavaScript 来实现向标头添加Token。
Authorization: Bearer <token>
此外,可以使用 jsonwebtoken 库中的 jwt.decode()
函数来解码此 Token
并在应用程序中使用有效负载数据。
4. 当用户注销时,需要手动从其存储中删除Token
一旦用户退出系统,需要手动清除存储在存储中的 Token
,使其无法用于进一步的请求。
Token的认证特征及优缺点
一种无状态机制
与 Cookie
不同,基于令牌的方法是无状态的。这意味着它不会在数据库或服务器中保存有关用户的任何信息。
服务器只负责创建、验证令牌,这允许构建比基于 Cookie 的方法更具可扩展性的解决方案。
安全问题
尽管令牌试图解决 Cookie
中的安全问题,但它也并不完全安全。
如果应用程序允许将外部 JavaScript 嵌入到应用程序中,则保存在浏览器中的令牌可能容易受到 XSS 攻击。
此外,由于令牌是无状态的,如果暴露在外面,在它到期之前没有办法撤销它。因此,尽可能少地保留令牌至关重要。大部分身份验证服务将 JWT
令牌的有效期设置在 5
分钟以内。
总结
基于令牌和基于 Cookie
的方法是 Web 应用程序最常用的两种身份验证机制。在本文中,讨论了它们的工作原理、特性、优缺点。
正如所看到的,这些方法都不是 100% 完美的,它们各有优缺点。因此,在选择身份验证方法时,建议根据项目要求选择一种,而不是追求完美的方法。