⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 www.dustyblog.cn 「Van」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

现在几乎大部分的 App 都支持使用多个第三方账号进行登录,如:微信、QQ、微博等,我们把此称为多账号统一登陆。而这些账号的表设计,流程设计至关重要,不然后续扩展性贼差。

本文不提供任何代码实操,但是梳理一下博主根据我司账号模块的设计,提供思路,仅供参考。

一、 自建的登陆体系

1.1.1 手机号登陆注册

该设计的思路是每个手机号对应一个用户,手机号为必填项。

流程:

  1. 首先输入手机号,然后发送到服务端。先判断该手机号是否存在账号,如果没有,就会生成随机验证码,将手机号和验证码绑定到 Redis中,并设置一定的过期时间(过期时间一般是5分钟,这就是我们一般手机验证码的有效期),最后将验证码通过短信发送给用户。
  2. 用户接收到验证码后,在界面填写验证码以及密码等基础信息,然后将这些数据发送服务端。服务端收到后,先判断在 Redis里面这个手机号对应的验证码是否一致,,失败就返回错误码,成功就给用户创建一个账号和保存密码。
  3. 注册成功后,用户即可通过自己的 手机号+密码进行登陆。

问题:

  1. 用户体验差,需要完成获取验证码,填写验证码/密码/用户名等诸多的信息完成注册,然后才能使用;
  2. 容易遗忘密码,遗忘后,只能通过忘记密码来重新设置密码。

1.1.2 优化注册登陆

该方案的思路是弱化密码的必填性,即无论用户是否注册过,可通过 手机号+验证码 直接进行登陆(保留 手机号+密码登录的方式)。

流程:

  1. 输入手机号,然后发送到服务端。服务端生成随机验证码,将手机号和验证码绑定到 Redis中,并设置一定的过期时间(过期时间一般是5分钟,这就是我们一般手机验证码的有效期),最后将验证码通过短信发送给用户。
  2. 用户接收到验证码后,在界面只需填写收到的验证码,提交到服务端。服务端收到后,先判断在 Redis里面这个手机号对应的验证码是否一致,失败就返回错误码,成功就直接登录。如果是老用户,直接拉取用户信息;如果是新用户,则提示他可以完善用户信息(不强制)。
  3. 用户通过 手机号+验证码登录后,也可选择设置密码,然后就可以通过 手机号+密码的方式登录,即:密码是非必填项。

用户表设计:

id user_name user_password user_mobile state more
用户id 用户名 用户密码 手机号码 账号状态 其他信息

1.2 引入第三方账户方案

1.2.1 微博登录

进入 Web2.0 时代 ,微博开放了第三方网站登录, 产品说, 这个我们得要, 加个用微博帐号就能登录我们的 App吧,而且得和我们自己的用户表关联。

流程:

  1. 客户端调用微博登录的界面,进行输入用户名、密码,登录成功后,会返回 access_token,通过 access_token调取 API接口获取用户信息。
  2. 服务端通过用户信息在我们用户表创建一个账号,以后,该第三方账号即可通过该微博账号直接进行登陆。

微博用户信息表设计:

id user_id uid access_token
主键id 用户id 微博唯一id 授权码

1.2.2 噩梦来临

紧接着, QQ又开放用户登录了, 微信开放用户登录了,网易开发用户登录了。。。。。。一下子要接入好多家第三方登录了, 只能按照 “微博用户信息表” 新建一个表,重写一套各个第三方登录。

二、 优化账号体系

2.1 原账号体系分析

  1. 自建登陆体系:无论 手机号+密码 , 还是 手机号+验证码 , 都是一种 用户信息+密码 的验证形式;
  2. 第三方登录:也是 用户信息+密码 的形式, 用户信息即第三方系统中的 ID(第三方系统中的唯一标识), 密码即 access_token, 只不过是一种有使用时效定期修改的密码。

2.2 新的账号体系

2.2.1 数据表设计

用户基础信息表:

id nickname avatar more
用户id 昵称 头像 其他信息

用户授权信息表:

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

说明:

  1. 用户表分为 用户基础信息表 + 用户授权信息表
  2. 用户信息表不保存任何密码, 不保存任何登录信息(如用户名, 手机号, 邮箱), 只留有昵称、头像等基础信息; 所有和授权相关,都放在用户信息授权表, 用户信息表和用户授权表是一对多的关系

2.2.2 登录流程

  • 手机号+验证码

沿用之前的方案。

  • 邮箱/手机号+密码:

用户填写 邮箱/手机号+密码; 请求登录的时候, 先判断类型, 如手机号登录为例:

使用 type='phone' 结合 identifier='手机号' 查找, 如有, 取出并判断 password_hash(密码)是否和该条目的 credential 相符, 相符则通过验证, 随后通过 user_id 获取用户信息;

  • 第三方登录, 如微信登录:

查询 type='weixin' 结合 identifier='微信 openId', 如果有记录, 则直接登录成功, 并更新 token; 假设与微信服务器通信不被劫持的情况下无需判断凭证问题。

2.2.3 优缺点

优点:

  1. 登录类型无限扩展, 新增登录类型的开发成本显著降低;
  2. 原来条件下, 应用需要验证手机号是否已验证和邮箱是否已验证, 需要相对应多一个字段如 phone_verifiedemail_verified, 如今只要在 用户授权信息表 表中增加一个统一的 verified字段, 每种登录方式都可以直观看到是否已验证情况;
  3. 用户授权信息表 添加相应的时间和 IP 地址, 就可以更加完整地跟踪用户的使用习惯, 比如:已经不使用微博登录两年多, 已经绑定微信 300天;
  4. 如果你说邮箱和手机号就是用户信息的组成部分, users 表尽管拓展, users 表里依然有email , phone , 但他们仅仅作为“展示用途”,和昵称,头像或者性别这些属性没有本质区别;
  5. 可按需绑定任意数量的同类型登录方式, 即一个用户可以绑定多个微信, 可以有多个邮箱, 可以有多个手机号。当然你也可以限制一种登录方式只有一条记录;

缺点 :

  1. 用户同时存在邮箱、用户名、手机号等多种站内登录方式时, 改密码时必须一起改, 否则就变成了 邮箱+新密码, 手机号+旧密码都可以登录, 肯定是很诡异的情况;

  2. 代码量增加了, 有些情况下逻辑判断增加了, 难度增大了; 举个例子, 无论用户是否已登录, 无论用户是否已注册过, 都是点击同一链接前往微博第三方授权后返回, 可能出现几种情况:

    1. 该微博在本站未注册过, 很好, 直接给他注册关联并登录;
  3. 该微博已经在本站存在, 当前用户未登录, 直接登录成功;

  4. 该微博未在本站注册, 但当前用户已经登录并关联的是另一个微博帐号, 作何处理取决于是否允许绑定多个微博帐号;

  5. 该微博未在本站注册过, 当前用户已登录, 尝试进行绑定操作;

  6. 该微博已经注册, 用户又已使用该帐号登录, 为何他重复绑定自己;

  7. 该微博已经在本站存在, 但当前用户已经登录并关联的是另一个微博帐号, 作何处理?

三、 一键登陆

3.1 背景

回顾一下 手机号+验证码 的登录方式:

  1. 输入手机号、等待验证码短信、输入验证码、点击登录。整个流程走完可能需要 20 秒以上,操作也比较繁琐;
  2. 它是依赖短信网络的,因为如果收不到短信,也就登录不了了。
  3. 从安全角度考虑,还存在验证码泄漏的风险。如果有人知道了你的手机号,并且窃取到了验证码,那他也能登录你的账号了。

但回过头来想一下,为什么我们需要验证码?验证码的作用就是确定这个手机号是你的,那除了使用短信,是否还有别的方式对手机号进行认证?

  1. 如果能获取到当前使用的手机号,就能对用户输入的号码进行验证了。但出于安全考虑,客户端是无法直接获取到手机号的,运营商则可以通过 SIM 卡数据查询到。
  2. 现在运营商已经开放了相关的能力,现在我们可以在用户输入手机号后,通过调用运营商的接口,判断用户输入的手机号是否和本地号码一致。这样一来,用户就省去了等待验证码短信、输入验证码的过程,也不受短信网络的限制,简化了登录流程。
  3. 但再进一步想,如果运营商可以把当前的号码直接返回给我们,而不只是用于验证,那用户连手机号都不需要填了。

这就是该部分的主角:一键登录

3.2 本机号码认证

获取到当前手机使用的手机卡号,直接使用这个号码进行登录,这就是一键登录。

这种登录方式的好处是显而易见的。它可以更方便、快捷地完成注册、登录流程,将原本可能需要 20 秒的流程,缩短到了 2 秒左右,很大程度上提升了登录的用户体验。

主要步骤如下:

  1. SDK 初始化:调用 SDK 的初始化方法,传入项目在平台上的 AppKey 和 AppSecret。
  2. 唤起授权页:调用 SDK 唤起授权接口。SDK 会先向运营商发起获取手机号验码的请求,请求成功后跳转到授权页。授权页会显示手机号掩码以及运营商协议给用户确认。
  3. 同意授权并登录:用户同意相关协议,点击授权页面的登录按钮,SDK 会请求本次取号的 token,请求成功后将 token 返回给客户端。
  4. 取号:将获取到的 token 发送到我们自己的服务器,由服务器携带 token 调用运营商一键登录的接口,调用成功就返回手机号码了。服务器用手机号进行登录或注册操作,返回操作结果给客户端,完成一键登录。

目前阿里云已经提供了该方式并可兼容三大运营商的号码,详见 阿里云SDK:

https://help.aliyun.com/document_detail/121113.html

四、小结

博主看来,没有最好的方案,选择适用当前系统的设计即可。不要深究孰优孰劣,鞋合不合脚,只有脚知道。

文章目录
  1. 1. 一、 自建的登陆体系
    1. 1.0.1. 1.1.1 手机号登陆注册
    2. 1.0.2. 1.1.2 优化注册登陆
  2. 1.1. 1.2 引入第三方账户方案
    1. 1.1.1. 1.2.1 微博登录
    2. 1.1.2. 1.2.2 噩梦来临
  • 2. 二、 优化账号体系
    1. 2.1. 2.1 原账号体系分析
    2. 2.2. 2.2 新的账号体系
      1. 2.2.1. 2.2.1 数据表设计
      2. 2.2.2. 2.2.2 登录流程
      3. 2.2.3. 2.2.3 优缺点
  • 3. 三、 一键登陆
    1. 3.1. 3.1 背景
    2. 3.2. 3.2 本机号码认证
  • 4. 四、小结