本文节选自《你必须懂的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。