登录系统设计
🔥 面试官问"设计一个登录系统",你还在说"建张用户表查一下就行了"?
那八成得凉
写在前面
这是一次网易二面的真题复盘。面试官抛出这道题的时候,我脑子里蹦出来的第一个东西就是一张 user 表和一个 password_hash 字段。
结果嘛——落榜了。不算冤,因为我确实没准备好这种层级的题。
事后回想,这场面试的败笔其实从一面结束前的反问环节就埋下了。我那时候问的是"公司有什么福利?"“加班多不多?”——这些当然重要,但面试官听完也就客套几句就过去了。他真正想听到的应该是:“请问贵部门目前的技术栈和业务方向是什么?如果我能进入二面,需要重点了解哪些领域?”
一旦你知道了部门的业务方向,二面的业务场景题就不是盲猜了。比如这道登录系统题,如果你在一面结束时了解到"我们部门在做全球化多端统一登录平台",那你回去就可以针对性准备:分布式 Session、OAuth 打通、跨区域账号合并……这些全都能提前想到。
所以给兄弟们提个醒:一面反问环节是情报战,不是福利调研。 了解业务方向之后,二面你至少能预判 60% 的题。
这篇文章就是把我复盘出来的解法写下来,希望对后面面试的兄弟有帮助。如果你也有类似的经历或者踩过的坑,欢迎分享出来,大家一起补全这张面试地图——评论区见 🐙
网易二面真题重现:
“我们有上百台服务器分布在北京、上海、新加坡、法兰克福。用户可以选微信、Google、GitHub、Apple ID 等多种第三方登录。你怎么保证同一个人用不同平台登录时,系统能认出他是同一个人?怎么避免重复注册?”
如果你听到这个问题,第一反应是"建个 user 表、查一下密码就完事了"——恭喜你,你已经踩进了面试官挖好的大坑里。
来,手把手带你拆一下这道题到底在考什么。
一、这题到底在问什么?
先理一下题面里埋的雷:
| 要素 | 背后的考点 |
|---|---|
| 上百台服务器分布在多个地区 | 分布式架构、数据一致性、异地多活 |
| 多种第三方登录 | OAuth 2.0 / OIDC 协议、账号绑定方案 |
| 同一个人用不同平台登录 → 识别为同一账号 | 账号打通、统一身份体系、映射关系设计 |
| 避免重复注册 | 幂等设计、分布式锁、全局唯一约束 |
面试官根本不是想听你背"登录流程三步走"——他想看看你有没有全局架构视野。
在动手拆解之前,先上一张整体架构图,方便你脑海里先搭个框架:

从客户端 → 网关层 → OAuth 认证 → 业务服务 → 数据层,一条龙串起来
二、架构基石:统一身份域(Identity Domain)
2.1 不要用 user_id 直接对外
最常见的错误设计:微信注册 → user_id = 1001,保存 wechat_openid=xxx;Google 注册 → user_id = 1002,保存 google_sub=xxx。
结果同一个用户发现自己在系统里有两个账号,而且它们之间的数据完全隔离,合都合不起来。
正确的做法:引入 Identity Domain 概念。
┌─────────────────────────────────────┐
│ User Account │
│ user_id (全局唯一, 自增/UUID) │
│ username, avatar, email, phone... │
│ created_at, status... │
├─────────────────────────────────────┤
│ ↑ 1 : N │
├─────────────────────────────────────┤
│ Identity (认证身份) │
│ identity_id, user_id │
│ identity_type: "wechat" / "google" │
│ identity_value: openid / sub │
│ verified: true/false │
└─────────────────────────────────────┘
核心思想: User(用户实体)与 Identity(认证身份)是一对多的关系。
一个人不管绑了多少个第三方账号,最后都指向同一个 user_id。
2.2 数据库表设计(简版)
-- 核心用户表
CREATE TABLE `user` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(64) DEFAULT NULL,
`avatar_url` VARCHAR(512) DEFAULT NULL,
`email` VARCHAR(255) DEFAULT NULL,
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1=正常 0=禁用',
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 第三方身份映射表
CREATE TABLE `user_identity` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` BIGINT UNSIGNED NOT NULL,
`identity_type` VARCHAR(32) NOT NULL COMMENT 'wechat | google | github | apple',
`identity_value` VARCHAR(255) NOT NULL COMMENT 'wechat openid / google sub / github id',
`raw_profile` JSON DEFAULT NULL COMMENT '第三方返回的原始信息,备用',
`verified` TINYINT NOT NULL DEFAULT 1,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_identity` (`identity_type`, `identity_value`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
关键点: (identity_type, identity_value) 联合唯一索引——这是"不重复注册"的最后一道防线。
三、账号打通:登录流程设计
3.1 首次登录(注册 + 绑定)
用户点击"微信登录"
↓
重定向到微信 OAuth 授权页面
↓
用户授权后,微信返回 code
↓
后端用 code 换取 access_token + openid
↓
后端查找 user_identity 表:
WHERE identity_type='wechat' AND identity_value='{openid}'
↓
┌─ 没找到? ─────────────────────────┐
│ 用 openid 等生成临时 token │
│ → 走"补充信息"流程 │
│ → 创建 user 记录 │
│ → 创建 user_identity 记录 │
└────────────────────────────────────┘
┌─ 找到了? ─────────────────────────┐
│ 返回已有 user 的登录态 │
└────────────────────────────────────┘
3.2 绑定更多平台(同一个用户)
用户已用微信登录,去到"绑定Google账号"页面
↓
Google OAuth 授权后拿到 sub(Google 的用户唯一标识)
↓
post /api/v1/account/bind 请求体:{ identity_type: "google", identity_value: "{sub}" }
↓
验签当前用户登录态 → 确认 user_id
↓
INSERT INTO user_identity (user_id, identity_type, identity_value) ...
如果 (google, sub) 已经绑了其他用户 → 抛异常:该Google账号已被绑定
如果成功 → 绑定完成
关键点: 这里的 user_identity 表记录了 user_id,所以后续不管用户从哪个平台登录,最终都能回到同一个 user_id。
3.3 交叉场景:B 平台已注册,A 平台首次登录
这个是面试高频追问:
用户先拿 Google 注册了账号 A,过两天用同一台手机的微信登录,怎么发现是同一个用户?
方案有以下几种(面试加分项):
| 方案 | 原理 | 优缺点 |
|---|---|---|
| 手机号兜底 | 首次登录引导绑定手机号,另一个平台登录时也用手机号认领 | ✅ 最可靠,❌ 用户可能拒绝 |
| 邮箱匹配 | 多个 OAuth 返回的 email 一致 → 提示合并 | ✅ 体验好,❌ 邮箱不一定相同或公开 |
| 主动合并 | 用户登录后手动发起"合并账号"操作 | ✅ 安全可控,❌ 流程长 |
| 设备指纹 | 同一设备短期内登录不同账号 → 提示是否合并 | ✅ 黑科技感,❌ 误判率高 |
最佳实践:组合使用——手机号兜底 + 邮箱提示 + 主动合并入口,三层保障。
四、分布式场景:异地多活的挑战
题目说了"上百台服务器分布在多个地区",那你的架构就不能是单库单表了。
4.1 用户 ID 生成:全局唯一
不能依赖数据库自增 ID(分库后重复)。
解决方案:
- Snowflake / 雪花算法 — 最常用,64位,按时间戳 + 机器号 + 序列号生成
- UUID v7 — 按时间排序的 UUID,适合新项目
- 美团 Leaf / 百度 UidGenerator — 工业级方案
4.2 幂等注册:防止重复插入
分布式场景下的并发问题:用户同时点了两次"登录",两个请求落到不同服务器,同时查到 user_identity 不存在 → 同时插入 → 重复注册。
防御手段(三层):
第1层:数据库唯一索引(最后防线)
UNIQUE KEY uk_identity (identity_type, identity_value)
谁先插入成功,谁就赢了;第二个 INSERT 直接抛 DuplicateKeyException
第2层:分布式锁
以 identity_type + identity_value 为锁 key,使用 Redis Redlock
tryLock → 查 DB → 插入 → unlock
避免在加锁期间重复请求打到数据库
第3层:业务幂等 Token
OAuth code 是一次性的,理论上不会重复请求
但可以额外用 code 作为幂等键(一码一用)
4.3 跨区域数据同步
上海用户用微信登录,数据写入上海机房;后来他去新加坡出差,用同一微信登录,请求到了新加坡机房——新加坡怎么知道这个用户已经注册了?
方案选型:
| 方案 | 说明 | 适用场景 |
|---|---|---|
| 全局中心数据库 | 所有区域读写同一个数据库集群(跨区域主从同步) | 数据量不太大、延迟可接受 |
| 单元化架构 | 用户按 hash 分到固定单元,该单元内有完整数据 | 大厂常用,复杂度高 |
| 最终一致性同步 | 各区域本地写入,通过 MQ / CDC binlog 同步到其他区域 | 允许短时间不一致 |
| 用户归属固定 | 注册时按地理位置或 hash 分配一个"归属区域",以后该用户的请求都路由到归属区域 | 需要全局路由层 |
面试推荐回答框架:
“我们采用用户归属固定 + 全局缓存的方案。用户首次注册时按 identity 的 hash 分配到最近的区域节点,身份信息写入该区域主库,同时异步同步到其他区域的只读副本。后续登录请求先查本地只读副本,如果有记录就走本地认证;如果没有,则根据 hash 路由到归属区域去查。同时我们在 Redis 全局缓存中存放
identity → user_id的映射,TTL 设置为 24 小时,大幅减少跨区域查询的次数。”
五、更多面试官可能追问的难点
5.1 Session 怎么共享?
千万别说用 Session + Cookie。分布式环境下 Session 不能存在单机内存里。
- ✅ 方案:JWT(JSON Web Token) — 无状态,客户端持有,服务端验签即可,不需要共享存储
- ✅ 方案:集中式 Session 存储 — Redis / Memcached 存储 Session,让所有服务器都去同一个 Redis 集群读
💡 组合使用:JWT 做认证令牌 + Redis 做短时效的 refresh token 存储,兼顾性能与安全性。
5.2 OAuth 第三方账号解绑/换绑
- 用户解绑最后一个第三方账号怎么办?——必须绑定至少一个登录方式,否则账号会"丢失"
- 换绑:解绑 A 平台,绑定 B 平台,期间需要引导用户确认身份(发验证码或密码验证)
5.3 第三方平台 openid 不一致
- 同一家平台,不同 AppID 返回的 openid 不一样 → 设计
identity_type为wechat_appid_xxx粒度 - 微信开放平台和公众号的 openid 也不同 → 同一 user 可以绑多个微信身份
5.4 账号合并与数据冲突
如果用户分别在 A 平台(手机号 138xxx)和 B 平台(手机号 139xxx)注册了两个账号,后来证明是同一个人:
- 用户手动发起合并
- 自动合并规则:保留主账号,副账号的 data 迁移到主账号下
- 冲突数据(如同名的收藏夹)加后缀或让用户手动选
5.5 安全相关
| 问题 | 应对 |
|---|---|
| 第三方 token 泄露 | 每个用户存 refresh_token,定时刷新,检测异常使用模式 |
| CSRF 攻击 | OAuth 授权流程自带 state 参数防 CSRF |
| 撞库攻击 | 登录接口限流、验证码、异地登录告警 |
| 同一设备多账号 | 设备指纹 + 风控引擎,异常登录要求二次验证 |
六、总结 · 面试该怎么说
面试官问"设计一个登录系统",不要一上来就讲 SQL。
回答框架建议:
- 先问清楚边界:“请问系统是单服务器还是分布式?支持哪些登录方式?”
- 画数据模型:User + Identity 两张表,解释为什么一对多
- 讲登录流程:首次注册 → 补充信息 → 绑定更多平台
- 讲账号打通:手机号兜底 + 邮箱匹配 + 主动合并
- 讲分布式挑战:全局 ID 生成、幂等注册、跨区域路由
- 补充 Session 与安全:JWT + Redis + 限流
这样一套下来,面试官会觉得你是有实战经验、有架构思维的候选人,不是只会 CRUD 的新手。
题外话:网易二面这道题的本质,其实是在问你——你平时写代码的时候,有没有想过业务背后的数据一致性和架构扩展性?如果你只是会写"select * from user where username=? and password=?",那确实还有很长的路要走。
全文完 · 如果觉得有帮助,欢迎收藏或转发给正在准备面试的朋友 🐙
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)