背景
一般来说大型互联网公司会把授权和用户信息的逻辑放到一个应用中,而这个应用我们统一称为用户中心。
用户中心不关心具体的业务逻辑,只处理用户信息相关的管理及授权登录。当第三方应用需要登录的时候,会把用户的登录请求转发到用户中心处理,处理完毕后,返回给第三方应用,第三方应用根据对应的凭证登录到系统内部。
主要功能如下
- 用户登录与注册
- 基本信息查询与修改
从功能来看,整个用户中心还是很简单单,不过其中的逻辑还挺复杂的,比如注册功能,就要分为手机注册与邮箱注册,手机要发送手机验证码,邮箱需要发送验证邮件,点击邮箱里面的链接跳转并进行后续注册流程,上面每步都需要业务上重新发送机制。
功能介绍
用户登录
在互联网用户中心体系中,一般会支持手机、邮箱、帐号、三方登录。其中三方登录一般会接入 QQ、微信、微博这三种方式。
密码登录
1. 用户在浏览器端填写 username + password ,然后提交到服务端
2. 服务端拿到用户提交的 username + password 验证。
3. 验证成功后,服务器返回请求,同时将 cookie 写到对应域
上述流程中,大家肯定会考虑到密码的安全性,我们到底该怎么做才能防止密码被泄露?对称加密还是非对称加密? 如果是对称加密,客户端被黑客反编译,就能拿到密钥,那么所有用户的密码就会存在非常大的泄露风险?如果是非对称加密,私钥要放在哪里才能保证安全?
通用简单的解决方案: Https + MD5 + 随机盐
Https 我就不用在述说了,基本上 Chrome、Firfox 都对不是 Https 的站点进行安全提醒,所以 Https 该上的还是尽快上吧
那如果公司很穷,买不起 Https 证书咋办呢?那么只能在前端页面上做点文章。
由于前端代码暴露在浏览器上,我们只能采用不可逆的密码或者摘要算法,类是与 MD5 / Hash 算法 。如果高级的话,就采用随机 Salt 来提高攻击成本,针对不同用户,加入不同的 Salt,而不是固定盐的方式。使用这种方式的前提「目前对安全的要求不高」
那我们该如何验证密码?客户端端提交 MD5(password)密码。服务端通过 MD5 (Salt + MD5(passowrd))的逻辑来计算最终密码,同时 Salt 只会出现在服务端,且每个用户采用不同 Salt 的方式来生成。这一系列过程中,都没有接触到原始的用户密码,如果出现用户的密码被劫持的话,只会发生在用户在提交密码前截获,这个也就是为什么需要密码控件?
三方登录
当用户以某种登录方式成功登录之后,我们能可以获取到对应 User 表中的用户基础信息,而登录操作只是为了认证用户这个过程,无论用本地密码验证还是第三方登录,以上过程本质上都是认证的形式。
所以用户的信息与登录的授权其实是独立开来的,即 uid 与 登录方式是一对多的关系。比如: 用户 A 使用「微信」登录,服务端认证身份后 uid = abc。而下一次用户 A 使用「微博」登录,同样服务端认证出来 uid = abc。
用户信息表(user_base)只存储用户 Profile 相关信息
id | name | city
----+------+-----------------
A1 | Tom | 上海
A2 | Jack | 背景
而本地密码验证可以当做一种授权方式,可以称为 local_auth 表
id | user_id | password
----+---------+----------
1 | A1 | qazwsx
2 | A2 | edcrfv
而通过微博登录就可以视作为另外一种登录方式,称为 weibo_auth 表
id | user_id | weibo_id | weibo_access_token | weibo_expires
----+---------+----------+--------------------+---------------
1 | A1 | W-qaz | xxxxxxxxxx | 604800
2 | A2 | W-wsx | xxxxxxxxxx | 604800
最后,如果还要增加一种登录方式的话,可以直接添加一直 xx_auth 表来存储用户认证信息,大大提高了我们授权方式的灵活性