user表的迭代设计之路(转发)

本文探讨了用户表设计的演进,从最初的简单结构到包含多种登录方式(包括第三方登录)的复杂结构。介绍了如何设计用户表以适应未来的需求,如支持身份证登录,以及如何通过将用户信息与授权信息分离,实现更灵活的登录方式管理和用户信息保护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本篇文章转发自:“Web项目聚集地”微信公众号,点击蓝色字体即可跳转。

用户表是每个应用/网站立项动工考虑的第一件事件;用户表结构的设计,算是整个后台架构的基石;如果基石不稳,待到后面需求跟进了发现不能应付,回过头来反复修改用户表,要大大小小作改动的地方也不少;与其如此,不妨设计用户表之初就考虑可扩展性,争取不需要太多额外代价的情况下一步到位:

先前设计

 1. id
 2. username
 3. password

用户名+密码,留个ID作为其他表的外键;当然,如果再考虑安全性,密码还需要通过加密存储,如:MD5。

后来,随着业务需求的拓展,要加个用户状态status判断用户是否被封禁,注册时间和注册IP地址上次登录时间和IP地址备查(并衍生出登录记录表,用来判断是否异地登录等),用户角色/权限role(又衍生出用户角色权限关系),业务也需要个人的个人信息如真实姓名,地址等,形成一个很完整的用户关系表:

id
username
password
realname
address
...
status
role
register_time
register_ip
login_time
login_ip

进入Web2.0时代,微博开放了第三方网站登录,于是加了一个微博用户登录表,并且与我们自己的用户表关联,这个微博用户信息表如下:

id      自增ID
user_id     关联本站用户ID
uid     微博唯一ID
access_token
access_expire

这还不算完,QQ也开放用户登录了,一下子要接入好多家第三方登录,只能就着“微博用户信息表”继续加类型加判断。

进入移动互联网时代,怎么也得支持个手机号登录吧,所以现在每家标配都是:用户名/邮箱/手机号登录,外加一系列微博、微信等第三方登录,表结构如下:
用户表

id
username
email
phone
...

用户第三方登录表

id
user_id
app_type
app_user_id
access_token
...

用户在输入框输入 用户名/邮箱/手机号和密码 之后,后台判断是邮箱,手机号或者用户名,再根据条件查询是否为特定用户。

这个表结构能够承载未来一段时间的业务需求了,如果说某天冒出了一个新的登录方式,比如身份证登录,除了继续在用户表加字段之外,可看以下改进版:

无论是username+password,还是phone+password,都是一种用户信息+密码的验证形式;再来理解第三方登录,其实它也是用户信息+密码的形式,用户信息即第三方系统中的ID(第三方登录一定会给一个在他们系统中的唯一标识),密码即access_token,只不过是一种有使用时效定期修改的密码;所以我们把它抽象出了 用户基础信息表+用户授权信息表 的形式:
用户基础信息表users

id
nickname
avatar

用户授权信息表user_auths

id
user_id
identity_type   登录类型(手机号/邮箱/用户名)或第三方应用名称(微信,微博等)
identifier      标识(手机号/邮箱/用户名或第三方应用的唯一标识)
credential      密码凭证(站内的保存密码,站外的不保存或保存token)

这个系统最大的特点就是:用户信息表不保存任何密码,不保存任何登录信息(如用户名,手机号,邮箱),只留有昵称,头像等基础信息;所有和授权相关(且基本前端展示无关的),都放在用户信息授权表,用户信息表和用户授权表是一对多的关系
例如:
users

|id|nickname|avatar|
|1|慕容雪村|http://.../avatar.jpg|
|2|魔力鸟|http://.../avatar2.jpg|
|3|科比|http://.../avatar3.jpg|

user_auths

|id|user_id|identity_type|identifier|credential|
|1|1|email|123@example.com|password_hash(密码)|
|2|1|phone|138888888|password_hash(密码)|
|3|1|weibo|微博UID|微博access_token|
|4|2|username|moliniao|password_hash(密码)|
|5|3|weixin|微信UserName|微信token|

具体处理:用户发来 邮箱/用户名/手机号和密码 请求登录的时候,依然是先判断类型,以某用户使用了手机号登录为例,使用SELECT * FROM user_auths WHERE type = 'phone' and identifier = '手机号'查找条目,如有,取出并判断password_hash(密码)是否和该条目的credential相符,相符则通过验证,随后通过user_id获取用户信息;

如果使用第三方登录,则只要判断SELECT * FROM user_auths WHERE type = 'weixin' and identifier = '微信UserName',如果有记录,则直接登录成功,使用新的token更新原token;假设与微信服务器通信不被劫持的情况下无需判断凭证问题。

优缺点

通过这个表结构设计 , 使许多原来纠结的问题瞬间解决 , 说说优点 :

  1. 站内登录类型无限拓展 , 代码改动小 ; 如果真要支持身份证登录了 , 只要少许几处改动 , 无需修改表结构 ;

  2. 第三方登录类型可用工场模式批量拓展 , 新增第三方登录类型的开发成本降到最低 ;

  3. 原来条件下 , 应用需要验证手机号是否已验证和邮箱是否已验证 , 需要相对应多一个字段如 phone_verified 和 email_verified , 如今只要在 user_auths 表中增加一个统一的 verified 字段 , 每种登录方式都可以直观看到是否已验证情况 ;
    · 基于信任第三方登录的数据准确性 , 默认第三方登录都是已验证 ; 如果用户修改登录手机号或登录邮箱 , 也能清晰跟踪每一步的完成度 ;

  4. 可按需绑定任意数量的同类型登录方式 , 即一个用户可以绑定多个微信 , 可以有多个邮箱 , 可以有多个手机号 , 是不是很赞 ? 当然你也可以限制一种登录方式只有一条记录 ;

  5. 在 user_auths 添加相应的时间和 IP 地址 , 就可以更加完整地跟踪用户的使用习惯 , 比如 , 已经不使用微博登录两年多 , 已经绑定微信 300 天

  6. 即使完全使用第三方帐号登录 , 可在前端做到 “无需注册本站帐号” 的效果 ; 过去许多网站虽然支持第三方帐号登录 , 但出于留存用户等原因 , 第一次微博登录回来 , 让你再填写一套他们网站的邮箱 , 密码等信息 , 也就失去了微博登录的最大意义 ;
    · 从技术上说 , 原有的结构导致除了在微博用户表建立一个条目外 , 必须在用户表建立一条对应的条目 , 而且一般情况下不能让用户表里的邮箱或者用户名和密码留空 ; 用户体验好的 , 邮箱自动生成微博ID@id.weibo.sina.com , 密码则随机生成 ;
    · 至于体验不好的 , 只能说早知道还不如不用微博登录呢 ! 现在呢 , 我们的这个用户表结构则完全没有这样的困扰 , 只要微博提供的昵称和头像地址就可以生成这个用户 , 再关联他的微博登录记录 ; 而且我们的表结构意味着 , 用户可以解除他的所有登录方式 , 于是这个账户变彻底变成了没法登录的僵尸 (解决办法是在代码里加一个限制 , 至少保留一条user_auths的记录) ;
    · 如果你非得得到用户的邮箱 , 那么每次登录的时候看到他不存在一条 identify_type 为 email 的记录 , 则弹窗弹死他 , 让他赶快填邮箱 , 否则啥都别干 ;

  7. 提升了逻辑思维能力 , 抽象出事物本质是码农必备职业素养 , 通过对用户表结构的学习研究 , 提高了鄙人的各方面技能 , 从此写代码一路顺风顺水…

  8. 如果你说邮箱和手机号就是用户信息的组成部分 , 他们依然需要体现在 users 表中作为前端展示?没问题 , users 表尽管拓展 , users 表里依然有email , phone , 但他们仅仅作为 “展示用途” , 和昵称 , 头像 , 或者性别这些属性没有本质区别 ;
    · 在用户信息表与用户授权登录拆分后 , 用户信息表可以随时增加任意字段 , 加星座 , 加生日 , 都没问题 , 只需要在前端展示时多几个输入框 , 录入时多几行代码 , 与用户登录相关的问题做到最大程度解耦 ;

有利必有弊 , 说说缺点 :

  1. 原先的用户判断由 1 次 SQL 变成 2 次 SQL 请求 ;

  2. 用户同时存在邮箱 , 用户名 , 手机号等多种站内登录方式时 , 改密码时必须一起改 , 否则就变成了 邮箱 + 新密码 , 手机号 + 旧密码访问了 , 肯定是很诡异的情况 ;
    · 如果考虑到这一点 , 又要在 user_auths 表中新增一个表示站内登录方式或第三方登录方式的标识字段 ;

  3. 代码量增加了 , 有些情况下逻辑判断增加了 , 难度增大了 ; 举个例子 , 无论用户是否已登录 , 无论用户是否已注册过 , 都是点击同一链接前往微博第三方授权后返回 ,
    可能出现几种情况 :
    1)该微博在本站未注册过 , 很好 , 直接给他注册关联并登录;
    2)该微博已经在本站存在 , 当前用户未登录 , 直接登录成功;
    3)该微博未在本站注册 , 但当前用户已经登录并关联的是另一个微博帐号 , 作何处理取决于是否允许绑定多个微博帐号;
    4)该微博未在本站注册过 , 当前用户已登录 , 尝试进行绑定操作;
    5)该微博已经注册 , 用户又已使用该帐号登录 , 为何他重复绑定自己 ;
    6)该微博已经在本站存在 , 但当前用户已经登录并关联的是另一个微博帐号 , 作何处理 ? 切换用户或是报错 ? (画一个流程图能更好描述这个问题) 这个问题与采用的数据结构没有关系 , 只是在做第三方帐号注册登录时遇到的各种情况 , 在此一并整理 ;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值