设计一个可扩展的用户登录系统 (1)

廖雪峰
资深软件开发工程师,业余马拉松选手。

在Web系统中,用户登录是最基本的功能。要实现用户名+密码登录,很多同学的第一想法就是直接创建一个Users表,包含usernamepassword两列,这样,就可以实现登录了:

id username password name,birth等其他字段
A1 bob a1b23f2c ...
A2 adam c0932f32 ...

现在问题来了,如果要让用户通过第三方登录,比如微博登录或QQ登录,怎么集成进来呢?

以微博登录为例,由于微博使用OAuth2协议登录,所以,一个登录用户会包含他的微博身份的ID,一个Access Token用于代表该用户访问微博的API和一个过期时间。

要集成微博登录,很多童鞋立刻想到把Users表扩展几列,记录下微博的信息:

id username password weibo_id weibo_access_token weibo_expires name,birth等其他字段
A1 bob a1b23f2c W-012345 a1b2c3d4 604800 ...
A2 adam c0932f32 W-234567 w5x6y7z8 604800 ...

加一个QQ登录Users表就又需要加3列,如果这么扩展下去,改表都得累死,不要说维护代码了。

那怎么才能设计出灵活的登录呢?

不妨换个角度考虑用户登录。当用户以任意一种方式登录成功后,我们读取到的总是Users表对应的一行记录,它实际上是用户的个人资料(Profile),而登录过程只是为了认证用户(Authenticate),无论是本地用密码验证,还是委托第三方登录,这个过程本质上都是认证。

所以,如果把Profile和Authenticate分开,就十分容易理解了。Users表本身只存储用户的Profile:

id name birth等其他字段
A1 Bob ...
A2 Adam ...

而通过用户名口令登录可视为一种Authenticate的方式,利用LocalAuth表维护:

id user_id username password
01 A1 bob a1b23f2c
02 A2 adam c0932f32

通过微博登录可视为另一种Authenticate方式,利用OAuth表维护:

id user_id weibo_id weibo_access_token weibo_expires
11 A1 W-012345 a1b2c3d4 604800
12 A2 W-234567 w5x6y7z8 604800

如果要添加另一种OAuth登录,比如QQ登录,增加一个表就可以了。不过既然大家都是OAuth家族的,不如统一到一个表,给每家起个名字区分就好了:

id user_id oauth_name oauth_id oauth_access_token oauth_expires
11 A1 weibo W-012345 a1b2c3d4 604800
12 A2 weibo W-234567 w5x6y7z8 604800
13 A1 qq Q-090807 q12-r34-s56 86400
14 A2 qq Q-807060 t78-u90-v12 86400

如果要增加一种新的登录方式,比如SAML,那就再加一种类型的表。

有些网站需要API访问,API可以使用api_keyapi_secret来认证,可是怎么把一个API访问关联到一个用户?方法还是增加一种APIAuth的表:

id user_id api_key api_secret
11 A1 a-012345 1qaz2wsx
12 A2 a-234567 3edc4rfv

每一种X-Auth表都存储了用户的登录认证信息,并通过user_id关联到Users表。这样一来,不但登录过程简化了,而且一个用户可以使用多种方式登录。只要登录成功,拿到了user_id,最后读取Users表是为了获得用户的Profile,这样读出来的数据也更安全,因为Users表不包含用户口令或Token信息,不会因为暴露API而不小心把口令给泄露出去。



Comments

Loading comments...