Skip to content
bitzorcas
中EN

Concept

Identity 模块架构概览:多租户身份中心与统一认证底座

深入解析 BitzOrcas.Modern 核心身份体系(Identity Module),掌握统一用户聚合根、多租户隶属关系模型、双因子认证与企业级单点登录(SSO/OIDC)。

Last updated

在现代企业级多租户 B2B SaaS 系统中,身份中心(Identity Subsystem)是整个系统的安全生命线:

  • 一人多企业(Multi-Tenancy Mapping):一个自然人用户(如财务专家或集团管理员)可能同时隶属于多个企业租户,在不同租户下拥有完全不同的角色与权限;
  • 企业级安全基线:不仅要防范暴力破解撞库、密码弱口令,还要提供开箱即用的 TOTP 双因子认证(2FA)、操作员模拟与企业级 SSO(SAML2 / OIDC)对接能力;
  • 高并发与无状态:登录校验毫秒级响应,Token 签发与刷新具备分布式黑名单阻断能力。

BitzOrcas.Modern Identity 模块采用垂直切片与严格的多租户聚合隔离设计,为整个平台提供坚不可摧的身份基石。

Identity 模块核心架构与交互拓扑

1. 终端客户端 (Web / Mobile / OpenAPI)

2. Identity API Endpoints (登录 / 注册 / 2FA / 切换租户)

3. 垂直切片 Handler (LoginCommandHandler / RegisterUserCommandHandler)

4. UserAggregate 领域聚合根 (密码哈希 / 状态机 / 租户绑定)

5. 安全组件 (IPasswordHasher: Argon2id, ITotpService)

6. ITokenIssuer (签发 AccessToken + 刷新令牌 RefreshToken)

7. CAP 领域事件 (user.registered / user.logged_in)


第一步:核心领域聚合根 UserAggregate

用户聚合根采用**统一聚合根(Unified Aggregate)**范式,直接映射物理表 SysUser:

UserAggregate.cs: 身份用户领域聚合根
using System;
using System.Collections.Generic;
using System.ComponentModel;
using BitzOrcas.Domain.Entities;
using BitzOrcas.Domain.Results;
using BitzOrcas.Persistence.Metadata;
namespace BitzOrcas.Identity.Domain;
public static class UserErrors
{
public static readonly Error InvalidEmail =
Error.Validation("User.InvalidEmail", "邮箱格式不正确。");
}
[BitzTable("SysUser", IsTenant = false, IsSoftDelete = true, Description = "平台全局用户主表")]
[BitzIndex("IX_SysUser_Email", nameof(Email), IsUnique = true)]
public sealed class UserAggregate : AggregateRoot<string>
{
[BitzColumn(Length = 120, IsRequired = true)]
public string Email { get; private set; } = string.Empty;
[BitzColumn(Length = 200, IsRequired = true)]
public string PasswordHash { get; private set; } = string.Empty;
[BitzColumn(Length = 50)]
public string PhoneNumber { get; private set; } = string.Empty;
// 用户全局状态:Active, Suspended, PendingActivation
[BitzColumn(IsRequired = true)]
public UserStatus Status { get; private set; } = UserStatus.PendingActivation;
// 是否启用了 TOTP 双因子认证
[BitzColumn(IsRequired = true)]
public bool IsTwoFactorEnabled { get; private set; }
// 租户关联子实体列表(记录该用户加入的所有企业租户及当前激活租户)
private readonly List<UserTenantMembership> _memberships = [];
public IReadOnlyCollection<UserTenantMembership> Memberships => _memberships.AsReadOnly();
[Obsolete("仅供 ORM 持久化物化使用。请使用 Create 工厂方法。", error: true)]
[EditorBrowsable(EditorBrowsableState.Never)]
public UserAggregate()
: base("0")
{
}
private UserAggregate(string id, string email, string passwordHash)
: base(id)
{
Email = email;
PasswordHash = passwordHash;
Status = UserStatus.Active;
}
// 领域工厂方法:创建新用户
public static Result<UserAggregate> Create(string email, string passwordHash, string initialTenantId)
{
// 1. 纯内存不变量校验
if (string.IsNullOrWhiteSpace(email) || !email.Contains('@'))
{
return Result<UserAggregate>.Failure(UserErrors.InvalidEmail);
}
var user = new UserAggregate(Guid.NewGuid().ToString("N"), email.Trim().ToLowerInvariant(), passwordHash);
// 2. 绑定初始企业租户
user._memberships.Add(new UserTenantMembership(initialTenantId, isDefault: true));
// 3. 产生用户注册领域事件
user.AddDomainEvent(new UserRegisteredDomainEvent(user.Id, user.Email, initialTenantId));
return Result<UserAggregate>.Success(user);
}
}

第二步:垂直切片写操作(LoginCommandHandler)

登录用例完全自包含,在 Handler 中完成凭据校验与令牌签发:

LoginCommandHandler.cs: 登录命令垂直切片
using System.Threading;
using System.Threading.Tasks;
using BitzOrcas.Application.Abstractions.Security;
using BitzOrcas.Domain.Results;
using BitzOrcas.Identity.Contracts;
public static class AuthErrors
{
public static readonly Error InvalidCredentials =
Error.Unauthorized("Auth.InvalidCredentials", "用户名或密码不正确。");
public static readonly Error AccountSuspended =
Error.Forbidden("Auth.AccountSuspended", "账户已被禁用或待激活。");
}
public sealed class LoginCommandHandler(
IUserRepository userRepository,
IPasswordHasher passwordHasher,
ITokenService tokenService)
{
public async ValueTask<Result<LoginResponseDto>> Handle(LoginCommand command, CancellationToken ct)
{
// 1. 根据标准化邮箱检索用户
var user = await userRepository.FindByEmailAsync(command.Email.ToLowerInvariant(), ct);
if (user is null)
{
// 恒定时间响应,防止用户枚举攻击
return Result<LoginResponseDto>.Failure(AuthErrors.InvalidCredentials);
}
// 2. 校验密码哈希(Argon2id 算法抗 GPU 破解)
var isPasswordValid = passwordHasher.Verify(command.Password, user.PasswordHash);
if (!isPasswordValid)
{
return Result<LoginResponseDto>.Failure(AuthErrors.InvalidCredentials);
}
// 3. 检查账户状态
if (user.Status != UserStatus.Active)
{
return Result<LoginResponseDto>.Failure(AuthErrors.AccountSuspended);
}
// 4. 签发多租户 JWT 与 Refresh Token
var tokenPair = await tokenService.GenerateTokensAsync(user, command.TenantId, ct);
return Result<LoginResponseDto>.Success(new LoginResponseDto(
AccessToken: tokenPair.AccessToken,
RefreshToken: tokenPair.RefreshToken,
ExpiresInSeconds: tokenPair.ExpiresIn));
}
}

总结与生产最佳实践

Identity 模块的设计准则:

  • 安全第一:密码哈希默认使用 Argon2id,绝不保存明文;
  • 多租户自适应:用户可在多个企业租户间无缝切换,无需重复注册多个邮箱;
  • 事件驱动解耦:用户注册、密码修改与登录审计通过领域事件异步分发。

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%