一、统一帐号体系业务及模型
本文节选自《你必须懂的18个互联网模型》已经发布在百度阅读平台,售价9.9元.购买地址
http://yuedu.baidu.com/ebook/977275a5767f5acfa0c7cd55
1 统一帐号体系业务及模型
1.1、场景描述
我们经常会碰到这样的需求,比如某企业需要做一个投诉报修的小系统,每个投诉报修的用户,如果他是第一次用微信登录我们的系统,那么需要输入手机号码,验证了这个手机号码后,才能进入我们的系统,提交报修信息。第一次报修后,以后用户再次提交报修信息,即可以自动识别该用户,免去登录的麻烦。我们的系统要能在微信里面运行,还要能在微博里面,浏览器里面运行,以后我们的系统还要做APP,用户同一帐号登录的APP和在微信微博里面登录的信息,应该是一样的。
类似于这类需求模型,我们都叫做统一帐号体系模型,他的大概整体流程是像下面这样的
该图重点在于统一登录流程,该流程的核心在于统一帐号体系,下面先来阐述统一帐号模型。
1.2、统一帐号核心模型
统一帐号模型的本质是以自有系统为基础,其他系统帐号标识ID与本系统相互绑定。一般的移动互联网体系中,核心帐号模型包含用户id,登录手机号、用户密码、头像、昵称、用户状态等,其他的信息,都可以在此基础上做扩展,如积分数目、用户等级、性别,年龄等,如果要使用邮箱、用户名等体系登录,我们还需要有独立的关联表,这些都在后面进行阐述。如下是统一账号基础模型。该模型中的id即为其他模型中的userid。
相关sql语句如下.
— —————————-
— Table structure for `account_user`
— —————————-
DROP TABLE IF EXISTS `account_user`;
CREATE TABLE `account_user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT ‘用户ID,自增长’,
`mobile` varchar(15) NOT NULL COMMENT ‘用户登录手机号’,
`passwd` varchar(40) NOT NULL COMMENT ‘用户密码,MD5加密,可以考虑策略时加盐’,
`nickname` varchar(20) DEFAULT NULL COMMENT ‘用户昵称’,
`avatar` varchar(150) DEFAULT NULL COMMENT ‘用户头像URL地址’,
`stat` varchar(10) DEFAULT ‘ACTIVE’ COMMENT ‘ACTIVE/FROZEN/DELETED帐号状态,正常,冻结,或者删除’,
PRIMARY KEY (`id`),
KEY `mobile` (`mobile`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=’统一登录主用户帐号信息表’;
注意事项:
本书sql语句目标服务器为mysql 5.0,本书数据库以UTF8编码。为了提高查询效率,我们通常会将mobile字段添加索引。为了保证系统安全,我们通常会将passwd加密存储,加密函数一般为MD5或者sha1。用户头像avatar字段一般不存储图片ID,因为该字段数据可能来自微信、微博等第三方社交媒体,是动态获取的。用户昵称nickname字段需要做好特殊字符的过滤工作,因为在微信微博等体系中,很多活跃用户会在自己的昵称中添加非打印字符,如果不过滤掉,sql语句可能会执行出错。该表读取频次远大于增改频次,一般用MyISAM做为引擎。一般来说,我们应该基于该表做缓存机制。
1.3、用微信等第三方平台登录
所谓用微信登录系统包括几个方面的内容,一是利用微信公众号在微信里面登录系统,二是利用APP调用微信授权接口登录APP,三是利用微信扫描登录PC站点,但是他们的模型是一样的,我们先以微信公众号为例说明。
1.3.1 在微信中利用微信公众号授权登录
微信公众号授权登录模型如下所示
相关sql语句如下
— —————————-
— Table structure for `account_weixin`
— —————————-
DROP TABLE IF EXISTS `account_weixin`;
CREATE TABLE `account_weixin` (
`openid` varchar(40) NOT NULL DEFAULT ” COMMENT ‘用户openid ‘,
`userid` int(11) NOT NULL COMMENT ‘该OPENID帐号绑定的用户ID’,
PRIMARY KEY (`openid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=’用户微信OPENID绑定信息表’;
— —————————-
— Records of account_weixin
— —————————-
备注说明:
openid 是每一个微信公众号对其下关注粉丝的唯一标识,对于同一用户,在不同公众号下面,openid是不一样的,对于同一公众号,不同用户的openid不一样。如果一个公司或者企业拥有多个公众帐号和app,都是支持微信授权登录机制,那么怎么识别这些用户是否为同一用户呢?在此微信引入了unionid 机制,对于同一开放平台帐号下面的微信公众号以及APP,网站应用等,同一微信用户的unionid 的是一致的。
1.3.2 利用微信开放平台支持登录
利用APP调用微信授权接口登录APP,以及利用微信扫描二维码登录PC站点都属于此类登录,此类登录的核心在于确定unionid和用户userid之间的关系。如下关于unionid的说明来自微信官方文档。
特别需要注意的是,如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号,用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。
如下是微信开放平台登录机制表,很简单的俩个字段
相关sql语句如下
— —————————-
— Table structure for `account_wxunion`
— —————————-
DROP TABLE IF EXISTS `account_wxunion`;
CREATE TABLE `account_wxunion` (
`unionid` varchar(40) NOT NULL DEFAULT ” COMMENT ‘用户在开放平台下的unionid’,
`userid` int(11) NOT NULL COMMENT ‘该OPENID帐号绑定的用户ID’,
PRIMARY KEY (`unionid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=’用户union id 绑定信息表’;
— —————————-
— Records of account_wxunion
— —————————-
备注说明:
关于微信开放平台详细信息请自行百度,相信登录代码请关注公众号itchuangyebuluo
1.3.3、第三方平台登录流程
第三方平台登录的核心原理和微信帐号登录是一致的,首先通过auth2授权,获得用户标识和基础信息然后,系统利用该用户标识查询到与之广联的用户ID然后登录。流程如下。
备注说明:
本流程核心在于将openid存储到session中,当用户第一次登陆时,由于openid尚未绑定userid,当用户名和密码匹配时,首先检测当前session中是否存在openid,如果存在,则将该openid和用userid相互绑定。
1.4、设备登录、邮箱登录及其他登录
1.4.1 利用设备ID登录帐号
每一个设备都有一个id,这个ID与account_device 关系表绑定后,每次打开应用后立即获取设备ID和用户的关联关系,即可实现登录。在实际过程中,我们可能会使用第三方的PUSH token 作为设备ID,push token 第三方推送平台用来标识设备的,这些token是经过处理过的,一般来说,每一个平台的push token 是不一致的。我们也可使用IMEI作为设备ID。设备ID登录适用于APP登录。相关表结构如下。
相关sql语句如下
— —————————-
— Table structure for `account_device`
— —————————-
DROP TABLE IF EXISTS `account_device`;
CREATE TABLE `account_device` (
`deviceid` varchar(40) NOT NULL DEFAULT ” COMMENT ‘该用户手机ID’,
`userid` int(11) NOT NULL COMMENT ‘该OPENID帐号绑定的用户ID’,
`bindat` datetime NOT NULL COMMENT ‘首次绑定时间’,
`lastoptat` datetime NOT NULL COMMENT ‘最后操作时间’,
PRIMARY KEY (`deviceid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=’终端设备和用户信息表’;
— —————————-
— Records of account_device
— —————————-
为了保证系统安全,我们一般会扩展俩个字段,一个是bindat,另一个是lastoptat,用作记录绑定的时间,和最后一次操作时间。如果绑定时间超过一定的时间,比如10天,系统需要自动解绑;如果最后一次操作时间与当前时间超出一定时间,比如10天,系统也需要自动解绑。此时用户需要重新绑定才能进行操作。
1.4.2 邮箱和用户名登录
邮箱和用户名登录体系比较简单,关键在于建立核心的关联关系。相关表结构如下所示。
用户名绑定比较简单,后端需要鉴定一下是否被使用就可以了,如果已经被使用,则绑定失败,如果尚未使用,则提示可以绑定。邮箱绑定稍微复杂一些,如下展示了邮箱绑定的一般流程。
邮箱绑定的关键在于鉴定签名是否合法。下面以实际列子来展示绑定的一般流程。
假设我们有个站点,域名地址为 test.com,则第一步,用户登陆后在邮箱绑定界面输入他自己的邮箱x@y.com,然后点击提交,服务器后端此时收到信息,首先检测邮箱帐号格式是否正确,以及x@y.com 是否被占用,如果被占用了,那么提示该邮箱已经被绑定,如果尚未占用,则构造类似于如下的信息发送到x@y.com邮箱中,用户点击如下连接即可跳转到绑定页面。
你好,这是一封来自xx的邮件,我们收到了你的邮箱绑定请求,请点击如下地址完成操作。
http://www.test.com/account/bindEmail.php?email=x@y.com&userid={userid}&createat={createat}&noisy={noisy}&sign={sign}.
在如上连接中有几点需要说明:
为了保证信息的安全性,我们采用了签名策略,签名字段sign是基于邮箱帐号email、要绑定的用户编号userid、邮件发送时间戳createat、噪音noisy以及加密因子apikey的函数。其中加密因子apikey是本系统内置的加密信息函数,不暴漏到外部。
createat的作用在于鉴定邮件是否过期,假设我们规定如果发送时间距离当前时间俩天以前,则为无效,则我们就可以根据createat时间戳和当前时间戳对比来判断。超出俩天的为非法邮件,不予绑定。
一般地,对于签名算法我们可以采用sha1或者md5策略,只要系统内部约定好即可,示例代码如下:
static $apikey =”abc”;
/*签名构造函数*/
function makeSign($email,$userId){
$noisy = time() . mt_rand() *1000 ;
$createat = time();
$str = “email={$email}&userid={$userId}&createat={$createat}&noisy={$noisy}&apikey={$apikey}”;
$sign = md5($str);
return $sign;
}
/*判断签名是否正确*/
function check($email,$userId,$noisy,$createat,$sign){
$str = “email={$email}&userid={$userId}&createat= {$createat}&noisy={$noisy}&apikey={$apikey}”;
//如果是俩天以前发的则直接认为过期
if($createat+3600*24*2<time()){
return false;
}
return $sign== md5($str);
}
相关sql如下
— —————————-
— Table structure for `account_email`
— —————————-
DROP TABLE IF EXISTS `account_email`;
CREATE TABLE `account_email` (
`email` varchar(40) NOT NULL DEFAULT ” COMMENT ‘邮箱帐号信息表’,
`userid` int(11) NOT NULL COMMENT ‘该邮箱帐号绑定的用户ID’,
PRIMARY KEY (`email`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=’用户邮箱绑定信息表’;
— —————————-
— Records of account_email
— —————————-
— —————————-
— Table structure for `account_name`
— —————————-
DROP TABLE IF EXISTS `account_name`;
CREATE TABLE `account_name` (
`name` varchar(40) NOT NULL DEFAULT ” COMMENT ‘用户用户名登录信息表’,
`userid` int(11) NOT NULL COMMENT ‘该name帐号绑定的用户ID’,
PRIMARY KEY (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=’用户登录帐号绑定信息表’;
— —————————-
— Records of account_name
— —————————-
1.5、长期在线及单点登录
1.5.1 长期在线的实现方式
长期在线的实现方式很多,其中一种可行的方式是用数据库存储session,此时数据库基础表需要做响应扩展,相应的流程如下。
用户第一次登录,系统侧登录成功后返回token,同时服务端将token和用户ID以及响应授权进行关联,此时客户端将token存储起来。以后,每次访问新的页面时,我们都只要携带该token即可。服务器端通过token识别该前端是否授权。一般地,为了安全,我们建议添加过期时间字段,用于刷新oken。长期在线相关表结构如下:
相关sql语句
— —————————-
— Table structure for `account_session`
— —————————-
DROP TABLE IF EXISTS `account_session`;
CREATE TABLE `account_session` (
`token` varchar(40) NOT NULL DEFAULT ” COMMENT ‘token 相当于sessionid’,
`userid` int(11) NOT NULL COMMENT ‘绑定的用户id’,
`bindat` datetime NOT NULL COMMENT ‘绑定时间’,
`lastoptat` datetime DEFAULT NULL COMMENT ‘最近一次刷新token的时间点’,
PRIMARY KEY (`token`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=’token和用户的对应关系’;
— —————————-
— Records of account_session
— —————————-
1.5.2 多点登录和单点登录
多点登录的本质可以理解成account_session 表中,1个userid 对应多个token,单点登录即一个userid 只能对应一个token.那么问题简单了。假设我们系统配置了单点登录,则用户A登录成功后获取了token1,用户B也使用了同样的帐号,登录后获取了token2,此时token1自动过期。多点登录则不会过期。此时token1和token2同时生效。
对于该表,我们要定期清除,因为随着我们系统访问量增加,该表记录会累计越来越多,最后数据库臃肿,定期清除可以给数据库减肥。另一方面,定期清楚可以增强系统的安全性。
定期清除的策略很多,可以根据lastoptat字段清除,每次用户登录的时候,我们 存储用户的登录时间到这个字段,如果检测到用户最近一次登陆时间相隔一定时间,则清除该记录。另一方面,如果bindat时间超过多长时间,则我们重新减除绑定关系。这些定时操作一般在后台进行。
多个设备同时在线是特殊情况,可以简单理解为设备ID即为token。