我们为仓库实现了一个完整的认证系统,而仓库只会做get和put操作

我们为仓库实现了一个完整的认证系统,而仓库只会做get和put操作

_

脱胎换骨构建身份认证:Azure 表存储之旅

身份认证是现代应用程序的基石。它是确保只有授权用户才能访问敏感数据、执行操作或与您的系统交互的机制。当我们思考构建此类系统时,首先想到的解决方案往往是关系型数据库。用户表、角色表、会话表、访问控制、OAuth 令牌——清单长得数不胜数。这是一种熟悉的模式,它得到了数十年的开发和丰富的工具支持。但如果你能使用一个几乎只提供键值对存储解决方案来构建一个健壮的身份认证系统,那会怎样?

这就是我们在 Azure 表存储上构建身份认证系统的故事,Azure 表存储是一个 NoSQL 数据存储,它提供分区键、行键,以及非常有限的其他功能。我们放弃了关系型数据库模型,转而选择在一个摒弃了连接、二级索引、自动递增字段和多行事务的系统上运行我们的身份认证逻辑。这是一个经过深思熟虑的决定,而理解它为我们带来了什么,需要更深入地审视所涉及的权衡。

传统方法:关系型数据库及其局限性

让我们首先承认传统智慧。关系型数据库(RDBMS)旨在处理复杂关系。它们擅长维护不同实体之间的引用完整性,例如用户及其角色、会话及其关联的令牌,或资源及其权限。外键、连接和事务等功能提供了一个强大的框架,用于确保数据一致性并执行访问规则。

考虑一个典型的身份认证流程:

  1. 用户登录:系统接收凭证。
  2. 验证:它查询用户表以检查凭证是否有效。
  3. 角色检索:如果有效,它将用户表与角色表连接起来以确定用户的权限。
  4. 会话创建:创建会话,可能链接到访问令牌和刷新令牌表。
  5. 授权:当访问资源时,系统检查会话和令牌表以验证授权。

这个流程在理论上很优雅,但在实践中却有其影响。每一步都涉及查询数据库,可能多次。连接可能很昂贵,尤其是在数据集增长时。二级索引通常对于高效的查找是必要的,但它们增加了复杂性和成本。虽然事务确保了多步操作的原子性,但它们可能资源密集型,并限制分布式系统中的可扩展性。

替代方案:Azure 表存储及其简洁性

Azure 表存储是一个 NoSQL 键值存储,旨在存储大量的结构化、非关系型数据。它提供了一个简单的架构,包含两个主要标识符:分区键和行键。给定分区内的数据是连续存储的,这使得访问速度快,但分区之间是隔离的,以确保可扩展性。没有连接、没有外键、没有复杂的索引选项,也没有跨多行的内置事务支持。

那么,为什么选择这个用于身份认证呢?答案在于约束以及它们如何塑造设计。

简约方法

没有连接,我们无法在一次操作中直接查询用户及其角色。相反,我们需要采用不同的策略。一种常见的模式是在用户的记录中嵌入或反规范化必要的数据。例如,我们可以将角色存储为逗号分隔的字符串,或存储在同一分区内的多个记录(尽管后者会使查找变得复杂)。

下面是一个简化的示例,说明 Azure 表存储中的用户记录可能看起来像什么:

{
  "PartitionKey": "Users",
  "RowKey": "user123",
  "Username": "johndoe",
  "PasswordHash": "hashed_password",
  "Roles": "admin,editor",
  "SessionId": null,
  "AccessToken": null,
  "RefreshToken": null
}

当用户登录时,系统对 Users 分区执行 GET 操作,使用 user123 行键。它检索用户数据,检查密码哈希,并从 Roles 字段确定角色。

处理会话和令牌

将会话信息直接存储在用户那里可能会随着会话数量的增加而变得杂乱无章。一种更具可扩展性的方法是将在单独的分区中存储会话数据,使用用户的 ID 作为分区键的一部分。这允许高效检索,同时将会话数据与用户资料数据隔离。

对于令牌,可以采用类似的策略。一个专用的分区可以存储访问令牌和刷新令牌,使用用户 ID 和令牌 ID 的组合作为分区键和行键。

{
  "PartitionKey": "Sessions",
  "RowKey": "user123_session456",
  "UserId": "user123",
  "SessionId": "session456",
  "ExpiresAt": "2023-10-27T12:34:56Z"
}

无连接的授权

授权变成了检查用户的角色与给定操作或资源的所需权限是否匹配的问题。这可以在检索用户角色后内存中完成,或者通过维护一个将资源映射到角色的单独权限表(同样使用键值结构)来完成。

权衡:我们获得什么和失去什么

选择 Azure 表存储而不是关系型数据库意味着接受一套不同的权衡。

优点:

  1. 可扩展性:像 Azure 表存储这样的 NoSQL 数据库通常设计为水平扩展,非常适合处理大量读写操作而不牺牲性能。
  2. 简洁性:缺乏复杂的特性(如连接和事务)可以简化数据模型并减少数据库操作的开销。
  3. 成本效益:NoSQL 存储可以更具成本效益,特别是对于具有简单数据需求和高可扩展性需求的应用程序。
  4. 灵活性:无模式特性允许在不需要复杂迁移的情况下轻松更新和更改。

缺点:

  1. 数据完整性:没有外键和事务,确保数据一致性需要仔细的应用层逻辑。
  2. 复杂查询:无法执行复杂连接意味着某些类型的查询可能难以甚至无法直接执行。
  3. 成熟度:虽然 NoSQL 数据库已经取得了长足的进步,但它们可能无法提供与成熟的 RDBMS 解决方案相同的成熟度和功能集。

实现注意事项

在 Azure 表存储上构建身份认证系统需要仔细规划和愿意从不同的角度思考数据建模和访问模式。以下是一些关键的考虑因素:

  1. 数据建模:设计您的架构以最大程度地减少对复杂查询的需求。在适当的地方使用反规范化,但要小心数据重复的潜在问题。
  2. 缓存:由于 Azure 表存储操作可能比查询关系型数据库慢,缓存频繁访问的数据可以显著提高性能。
  3. 一致性:没有事务,确保您的应用逻辑通过其他方式(如锁定机制或幂等操作)维护数据一致性。
  4. 安全性:永远不要存储明文密码。始终使用安全的哈希算法,如 bcrypt、scrypt 或 Argon2。为了更好的安全性,请考虑使用添加盐和其他保护的密码哈希库。

真实世界示例:一个身份认证服务

让我们通过一个简化的示例来了解如何使用 Azure 表存储实现身份认证服务。

用户注册

当新用户注册时,服务在 Users 分区创建一条记录:

INSERT INTO Users (PartitionKey, RowKey, Username, PasswordHash, Roles)
VALUES ('Users', 'user789', 'janedoe', 'hashed_password', 'user');

用户登录

登录流程涉及几个步骤:

  1. 检索用户数据:执行 GET 操作以检索用户数据。

    sql SELECT * FROM Users WHERE PartitionKey = 'Users' AND RowKey = 'user789';

  2. 验证密码:比较提供的密码哈希与存储的哈希。

  3. 生成令牌:如果密码正确,生成访问令牌和刷新令牌。
  4. 存储会话:在 Sessions 分区创建新记录。

    sql INSERT INTO Sessions (PartitionKey, RowKey, UserId, SessionId, ExpiresAt) VALUES ('Sessions', 'user789_session101', 'user789', 'session101', '2023-10-27T12:34:56Z');

  5. 存储令牌:在 Tokens 分区创建记录。

    sql INSERT INTO Tokens (PartitionKey, RowKey, UserId, AccessToken, RefreshToken, ExpiresAt) VALUES ('Tokens', 'user789_token202', 'user789', 'access_token_202', 'refresh_token_202', '2023-10-27T15:00:00Z');

  6. 返回令牌:将访问和刷新令牌返回给用户。

令牌刷新

当用户请求令牌刷新时,服务:

  1. 检索刷新令牌:对 Tokens 分区执行 GET 操作。

    sql SELECT * FROM Tokens WHERE PartitionKey = 'Tokens' AND RowKey = 'user789_token202';

  2. 验证令牌:检查令牌是否有效且未过期。

  3. 生成新令牌:如果有效,生成新的访问和刷新令牌。
  4. 更新令牌:用新记录替换旧令牌记录。

    sql UPDATE Tokens SET AccessToken = 'new_access_token', RefreshToken = 'new_refresh_token', ExpiresAt = '2023-10-28T12:34:56Z' WHERE PartitionKey = 'Tokens' AND RowKey = 'user789_token202';

  5. 返回新令牌:将新令牌返回给用户。

经验教训

在 Azure 表存储上构建身份认证系统是一次重新思考传统方法的旅程。它需要我们接受 NoSQL 数据存储的约束,并找到创造性的解决方案来解决在关系型数据库中用连接和事务可以轻松解决的问题。

关键的经验教训是:

  1. 约束驱动创新:限制可以迫使我们在长期内探索可能更具可扩展性或效率的替代设计。
  2. 数据建模至关重要:基于 NoSQL 的身份认证系统的成功取决于一个有效的数据模型,该模型最大限度地减少对复杂查询的需求。
  3. 权衡是过程的一部分:每个技术选择都涉及权衡。了解你获得什么和失去什么是做出明智决策的关键。
  4. 应用层逻辑很重要:没有数据库处理复杂操作,应用程序必须承担额外的责任来确保数据完整性和一致性。

总结

在 Azure 表存储上构建身份认证系统展示了约束驱动创新的力量。通过摒弃传统的关系型数据库模型,我们获得了可扩展性和简洁性,代价是失去了连接和事务等特性。关键在于设计一个最大限度地减少对复杂查询需求的数据模型,并实施强大的应用层逻辑来处理数据一致性和安全性。

这种方法突出了现代数据存储的灵活性,以及在设计系统时超越传统的重要性。无论您是在使用 NoSQL 还是关系型数据库,良好的设计原则都是相同的:了解您的数据,规划可扩展性,并优先考虑安全性。通过这样做,您可以构建不仅功能齐全,而且高效和弹性的身份认证系统。

我每个AI工具演示中关注的五个红旗(大多数买家都忽略了) 2026-07-10
设置后忘之:在Retool中安排每周数据同步 2026-07-10

评论区