Google用户账户密码设计实践
2021-05-05 13:29:43 阿炯

账户管理、授权和密码管理可能对于许多开发者来说是一个没有得到足够关注的黑暗角落。以下内容是Google云平台总结的12条最佳实践。

目录

对密码进行Hash
    密码加密时的加盐(salt)是什么?
    密码哈希的方法
    PBKDF2
    Argon2
    Bcrypt
    Scrypt
如果可能,允许用户使用第三方登陆
将用户身份和用户账户概念分隔开
允许多个身份链接到单个用户账户
不要阻止长密码或复杂密码
不要对用户名强加不合理的规则
允许用户更改其用户名
让用户可以删除他们的账号
慎重的考虑会话长度
使用两步验证
使用户ID不区分大小写
构建安全的身份验证系统
进一步阅读
相关文章

对密码进行Hash

最重要的账户管理规则是安全地存储用户的敏感信息,如密码。在任何时候都不要存储明文的密码,并且存储的密码要是无法解密还原的。常用的方式是使用PBKDF2、Argon2、Scrypt或Bcrypt创建对密码进行hash。另外hash时还要进行加盐处理。千万不要使用已经被弃用的hash技术,如MD5、SHA1,在任何情况下都不应使用可逆加密或尝试创建自己的散列算法。在设计系统时,需要问自己,假使数据库泄露了,泄露后是否会对用户使用的服务或者用户使用的其他网站的服务构成威胁,我们可以采取哪些措施来减少泄漏事件造成的损害?

密码加密时的加盐(salt)是什么?

我们知道,如果直接对密码进行散列,那么黑客可以对一个已知密码进行散列,然后通过对比散列值得到某用户的密码。换句话说,虽然黑客不能取得某特定用户的密码,但他可以知道使用特定密码的用户有哪些。

加Salt可以一定程度上解决这一问题。所谓加Salt,就是加点“佐料”。其基本想法是这样的——当用户首次提供密码时(通常是注册时),由系统自动往这个密码里撒一些“佐料”,然后再散列。而当用户登录时,系统为用户提供的代码撒上同样的“佐料”,然后散列,再比较散列值,已确定密码是否正确。

这里的“佐料”被称作“Salt值”,这个值是由系统随机生成的,并且只有系统知道。这样,即便两个用户使用了同一个密码,由于系统为它们生成的salt值不同,他们的散列值也是不同的。即便黑客可以通过自己的密码和自己生成的散列值来找具有特定密码的用户,但这个几率太小了(密码和salt值都得和黑客使用的一样才行)。


下面详细介绍一下加Salt散列的过程。介绍之前先强调一点,前面说过,验证密码时要使用和最初散列密码时使用“相同的”佐料。所以Salt值是要存放在数据库里的。


如上图所示,注册时:

用户提供密码(以及其他用户信息)
系统为用户生成Salt值
系统将Salt值和用户密码连接到一起
对连接后的值进行散列,得到Hash值
将Hash值和Salt值分别放到数据库中

如上图所示,登录时:


用户提供用户名和密码
系统通过用户名找到与之对应的Hash值和Salt值
系统将Salt值和用户提供的密码连接到一起
对连接后的值进行散列,得到Hash’(注意有个“撇”)
比较Hash和Hash’是否相等,相等则表示密码正确,否则表示密码错误

密码哈希的方法

PBKDF2

PBKDF2(Password-Based Key Derivation Function)是一个用来导出密钥的函数,常用于生成加密的密码。它的基本原理是通过一个伪随机函数(例如HMAC函数),把明文和一个盐值作为输入参数,然后重复进行运算,并最终产生密钥。如果重复的次数足够大,破解的成本就会变得很高。而盐值的添加也会增加“彩虹表”攻击的难度。


上图是14年在一台多GPU的高端PC上进行的测试,可以看到,在4个单词(随机从Diceware列表选择)的情况下,如果每秒能猜2万个密码,则要猜出密码平均需要2890年。如果密码的长度再高点,则这个时间是天文数字了。

PBKDF2函数的定义:DK = PBKDF2(PRF, Password, Salt, c, dkLen)

PRF是一个伪随机函数,例如HASH_HMAC函数,它会输出长度为hLen的结果。
Password是用来生成密钥的原文密码。
Salt是一个加密用的盐值。
c是进行重复计算的次数。
dkLen是期望得到的密钥的长度。
DK是最后产生的密钥。


Argon2

Argon2 是 PHC(Password Hashing Competition[3])的冠军,利用大量内存和大量计算资源进行 Hash 计算。提供三个版本:

Argon2d:更快,使用 data-dependent 的内存访问方式,data 是需要 Hash 的 password 和 salt。适合加密货币和不会收到 side-channel timing 攻击的应用。
Argon2i:使用 data-independent 的内存访问方式,更适合密码哈希等。他比 Argon2d 慢,因为它需要更多次内存计算(passes)来保护免受 tradeoff 的攻击。
Argon2id:是 Argon2i 和 Argon2d 的混合版本,第一次计算用 Argon2i,后续的计算用 Argon2d。如果没有特定的理由,推荐使用 Argon2id。


Argon2 的实现:

Primary inputs 是 message P 和 nonce S,代表密码和盐。
Secondary inputs 有
并行程度 p
Tag length τ, 代表期望返回的结果长度 bytes
内存大小 m kilobytes
迭代次数 t,用于调节运行时间,与内存大小独立
version number v
secret value K,默认没有 key
associated data X
Type y of Argon2, 0 代表 Argon2d, 1 代表 Argon2i, 2 代表 Argon2id

特性:

性能。Argon2 填充内存的速度非常快,从而增加了 AT 里的 A。Argon2i 对每个 byte 稳定占用 2 个 CPU cycles,Argon2d 就快 3 倍。
Tradeoff 抵御能力。默认 passes 配置下(Argon2d 为 1,Argon2i 为 3),ASIC 设备在减少至 α =1/4 或更少内存的时候,无法减少 time-area 复杂度。如果把 pass 数量提高,时间惩罚会更高。
可扩展性。Argon2 可以同时在时间和内存两个维度扩展,两者互相独立,保证始终能在一定数量的时间内填满一定数量的内存。
并行。Argon2 最多可以使用 2^24 个并发线程。在实验中,8 个线程就已经消耗完了所有的计算资源和带宽。
GPU/FPGA/ASIC-unfriendly。Argon2 对 x86 做了高度优化,所以在定制硬件下运行既不会更快也不会更便宜。
支持额外的输入。除了 message 和 nonce,类似 secret key,环境变量,用户数据等内容也可以被输入作为参数。

Bcrypt

Bcrypt是专门为密码存储而设计的算法,基于Blowfish加密算法变形而来,由Niels Provos和David Mazières发表于1999年的USENIX。Bcrypt最大的好处是有一个参数(work factor),可用于调整计算强度,而且work factor是包括在输出的摘要中的。随着攻击者计算能力的提高,使用者可以逐步增大work factor,而且不会影响已有用户的登陆。Bcrypt经过了很多安全专家的仔细分析,使用在以安全著称的OpenBSD中,一般认为它比PBKDF2更能承受随着计算能力加强而带来的风险。bcrypt也有广泛的函数库支持,因此我们建议使用这种方式存储密码。

Bcrypt有四个变量:

saltRounds: 正数,代表hash计算次数,数值越高越安全,默认10次。
myPassword: 明文密码字符串。
salt: 盐,一个128bits随机字符串,22字符
myHash: 经过明文密码password和盐salt进行hash,个人的理解是默认10次下 ,循环加盐hash10次,得到myHash

每次明文字符串myPassword过来,就通过10次循环加盐salt加密后得到myHash, 然后拼接BCrypt版本号+salt盐+myHash等到最终的bcrypt密码 ,存入数据库中。这样同一个密码,每次登录都可以根据自省业务需要生成不同的myHash, myHash中包含了版本和salt,存入数据库。

Bcrypt密码图解:


Scrypt

Scrypt是由著名的FreeBSD黑客 Colin Percival为他的备份服务 Tarsnap开发的。

Scrypt不仅计算所需时间长,而且占用的内存也多,使得并行计算多个摘要异常困难,因此利用rainbow table进行暴力攻击更加困难。Scrypt没有在生产环境中大规模应用,并且缺乏仔细的审察和广泛的函数库支持。但是,Scrypt在算法层面只要没有破绽,它的安全性应该高于PBKDF2和Bcrypt。


Scrypt 算法的工作原理:

首先用随机数据填充随机存取存储器 RAM 里面的缓存空间。
再从这块内存区域里虚拟随机地读取数据,同时要求整个缓存都存储在 RAM 里面。

Scrypt算法会产生一个p个块元素的数组,p的值大概比2^31(42亿)小几个数量级,实际使用中可能是十万~百万级别吧?对于每个块元素,都是进行一系列复杂运算生成的哈希值,最后对整个数组再进行PBKDF2-HMAC-SHA256运算得到最终结果。Scrypt算法保证只有将每个元素都存放在内存中,最后才能算出正确的结果,从算法层面保证了对大量内存空间的硬需求,从而提高了运算成本。

如果可能,允许用户使用第三方登陆

第三方登陆服务可以使您可以依赖受信任的外部服务来验证用户的身份。Google,Facebook和Twitter是常用的提供商。您可以使用Firebase Auth等平台在现有内部身份验证系统旁边实施外部身份提供程序。Firebase Auth带来了许多好处,包括更简单的管理,更小的攻击面和多平台SDK。我们将在此列表中触及更多优势。查看我们的案例研究,了解能够在短短一天内集成Firebase Auth的公司。

将用户身份和用户账户概念分隔开

您的用户不是电子邮件地址。他们不是电话号码。它们不是OAUTH响应提供的唯一ID。您的用户是他们在您的服务中独特,个性化数据和的体验。精心设计的用户管理系统在用户配置文件的不同部分之间具有低耦合和高内聚性。保持用户账户和凭证的概念分离将极大地简化实施第三方身份提供商的过程,允许用户更改其用户名并将多个身份链接到单个用户账户。实际上,为每个用户提供内部全局标识符并通过该ID链接其配置文件和身份验证标识可能比将其全部存储在单个记录中更优。

参考链接:用户系统设计:三户模型&三层身份模型

允许多个身份链接到单个用户账户

如果用户在一周前使用其用户名和密码进行了登陆,但在这次使用了Google登陆,用户不会意识到可能会创建重复的账户。同样,用户可能有充分的理由将多个电子邮件地址链接到您的服务。如果您正确地分离了用户身份和身份验证,则将多个身份链接到单个用户将是一个简单的过程。

您的后端需要考虑用户是否有可能在注册过程中完成部分或全部,然后才意识到他们正在使用未链接到系统中现有账户的新第三方身份。这最简单地通过要求用户提供公共识别细节(例如电子邮件地址,电话或用户名)来实现。如果该数据与系统中的现有用户匹配,则要求他们也使用已知身份提供商进行身份验证,并将新ID链接到其现有账户。

不要阻止长密码或复杂密码

NIST最近更新了密码复杂性和强度指南。随着你使用Hash算法对密码进行存储,中间的很多很多可以迎刃而解,hash算法会产生固定长度的输出,因此不必去限制用户韩剧密码的长度,如果必须限制密码长度,则只能根据服务器允许的最大POST大小来设置密码长度。这通常远高于1MB。

Hash出来的密码将由少量已知的ASCII字符组成(如果输出为二进制,则可以通过Base64进行转换)。考虑到这一点,您应该允许您的用户在密码中使用他们想要的任何字符。如果有人想要一个由Klingon、Emoji和两端都有空格的控制字符组成的密码,你应该没有技术理由否认它们。

不要对用户名强加不合理的规则

站点或服务要求用户名超过两个或三个字符,阻止隐藏字符并阻止用户名开头和结尾处的空格并不是不合理的。有些网站确实由这样的要求,例如最小长度为8个字符,或者阻止7位ASCII字母和数字之外的任何字符。对用户名进行严格限制的网站可能会为开发人员提供一些快捷方式,但这样做会以牺牲用户为代价,而极端情况会导致一些用户离开。

在某些情况下,只能做到分配用户名。如果您的服务属于这种情况,请确保指定的用户名是用户友好的,只要他们需要回忆和通信。字母数字ID应避免使用视觉上模糊的符号,例如“Il1O0”。另外建议您对任何随机生成的字符串执行字典扫描,以确保用户名中没有嵌入非预期的信息。这些相同的准则适用于自动生成的密码。

允许用户更改其用户名

在遗留系统或任何提供电子邮件账户的平台中,不允许用户更改其用户名,这种情况非常普遍。有充分理由不自动释放用户名以供重用,但系统的长期用户最终会提出使用其他用户名的充分理由,他们可能不想创建新账户。

您可以通过允许别名并让用户选择主别名来尊重用户更改用户名的愿望。您可以在此功能之上应用所需的任何业务规则。某些组织可能每年仅允许更改一个用户名,或阻止用户显示除主用户名之外的任何内容。电子邮件提供商可能会确保用户在从其账户中分离旧用户名之前充分了解风险,或者可能完全禁止取消旧用户名的链接。为您的平台选择正确的规则,但要确保它们允许您的用户随着时间的推移成长和变化。

让用户可以删除他们的账号

用户由充分的理由永久关闭账户并删除所有个人数据,但是令人惊讶的是大量的服务并没有提供这样的自助服务。这里需要考虑安全性与合规性的平衡,但大多数受监管的环境都提供了有关数据保留的特定指南。避免合规性和黑客攻击问题的常见解决方案是让用户选择是否进行删除。在某些情况下,法律上可能会要求您遵守用户要求及时删除其数据的请求。

慎重的考虑会话长度

安全性和身份验证经常被忽视的一个方面是会话长度。Google花了很多精力确保用户是他们本人,并会根据某些事件或行为进行仔细检查。用户可以采取措施进一步提高安全性。对于非关键分析目的,您的服务可能有充分理由保持会话无限期打开,但应该有阈值,之后您要求输入密码或其他用户验证。

考虑用户在重新进行身份验证之前应该能够处于非活动状态的时间。如果有人执行密码重置,请验证所有活动会话中的用户身份。如果用户更改其配置文件的核心方面或执行敏感操作时,则提示进行身份验证。考虑禁止从多个设备或位置登录同时是否有意义。

当您的服务确实使用户会话到期或需要重新身份验证时,请实时提示用户或提供一种机制来保留自上次身份验证以来未保存的任何活动。用户填写长表格,稍后提交并发现所有输入都已丢失并且必须再次登录,这是非常令人沮丧的。

使用两步验证

在选择两步验证(也称为双因素授权或仅2FA)方法时,请考虑用户对其账户被盗的实际影响。由于存在多个弱点,NIST已弃用SMS 2FA(短信二次验证),但是,它可能是您的用户可以接受的最安全的选项。启用第三方身份提供商并在其2FA上搭载是一种简单的方法,可以在不花费大量精力的情况下提高安全性。


大多数用户为了便利,常常将不同的网络服务设置了同样的账户、同样的密码。这很容易导致在某个地方泄漏了密码之后,他人可以“顺藤摸瓜”使用该账户以及密码轻易的登录到用户注册过的其他网络服务。

如果要将「密码」与「两步验证」做对比。

密码就像是「印章」一样,可能你会在不同网站使用同样的印章,甚至别人可以偷偷使用你的印章。
两步验证就像是「签名」一样,只有你本人才能使用。像提币这种重要的安全操作,就必须要「本人」才可以操作。


两步验证的原理。是手机两步软件

手机软件先根据网站提供的密钥,产生一次性密码
与网站配对绑定后,产生密钥种子。
手机每 30 秒会产生一组一次性的密码,可以通关该网站的验证机制


两步验证软件,通常业界最常见的有这三款:
Google Authenticator
Authy
身份宝

您可以选择一款绑定使用。

使用户ID不区分大小写


您的用户不在乎,甚至可能不记得用户名的确切情况。用户名应完全不区分大小写。将所有用户名和电子邮件地址全部以小写形式存储并将所有输入转换为小写进行比较之前是非常容易实现的。

越来越多的设备是智能手机,其中大多数手机提供纯文本字段的自动更正和自动大小写。在UI级别阻止此行为可能不是理想的或完全有效。

构建安全的身份验证系统

如果您使用的是Firebase Auth等服务,则会自动为您处理许多安全问题。但是,您的服务始终需要正确设计以防止滥用。核心考虑因素包括实施密码重置而不是密码检索,详细记录账户的活动,速率限制登录尝试,在多次不成功的登录尝试后锁定账户,以及对无法识别的设备或长时间闲置的账户要求两步身份验证。安全认证系统还有许多方面,因此请参阅以下部分以获取更多信息的链接。

进一步阅读

有许多优秀的资源可用于指导您完成开发,更新或迁移帐户和身份验证管理系统的过程。我推荐以下内容作为起点:
NIST 800-063B涵盖身份验证和生命周期管理
OWASP不断更新的密码存储备忘单
OWASP使用身份验证备忘单进一步详细说明
Google的Firebase身份验证网站拥有丰富的指南,参考资料和示例代码库

参考链接
12 best practices for user account,authorization and password management
Password Hashing: Scrypt, Bcrypt and ARGON2


本文转自钱魏Way的个人网站,感谢作者。


用户系统设计:三户模型&三层身份模型

三户模型

三户模型最早是在增强型电信运营图(Enhanced Telecom Operations Map,eTOM)中提出,在电信行业中得到广泛使用。 三户指客户(Customer)、用户(User)和账户(Account)。eTOM 引入是电信行业营销模型转向“以客户为中心”的理念而产生的成果。围绕客户建立用户和账户。这三个是相互关联的实体,这种关联只是一个归属和映射的关系,而三个实体本身是相互独立的,分别是体现完全不同的几个域的信息,客户是体现了社会域的信息,用户体现了业务域的信息,账户体现的是资金域的信息。


客户(customer):是指客户(自然人、公司、集团公司)的基本资料信息。例如自然人的姓名、手机号、身份证、邮箱地址等等;公司的五证一照、行业、联系人、网站地址、通讯地址等等。如无特指,一般客户指个人客户。

用户(user):指客户在系统的登录账号信息,包括账号、密码、人员权限、角色等等。对应的,法人客户在系统中注册后,被称之为商户。

账户(account):指客户在系统的虚拟账户,主要与交易记账相关。


以电信业务为例:
客户:人
用户:电信产品的实例表现
账户:付费的账号

假设张三是中国典电信的一个客户,张三办理了一个手机SIM卡和一个宽带业务。其中手机SIM卡和宽带业务分别作为一个用户存在(电信产品实例)。下面还有一个账户的概念,账户是用户进行付费的。一般情况,一个用户只有一个账户,以上例为例,张三有一个账户,为手机号和宽带付费。当然,有的人可能存在多个账户,例如手机号是一个账户,宽带是是一个账户。

总结一下,一个客户(张三)可以有多个用户(一个手机号和一个宽带业务),但一个用户只能归属于一个客户。一个客户可以用多个账户,但一个账户也只能归属于一个客户。账户和用户的关系比较复杂,一般情况,一个账户可以给多个用户付费,但也允许,一个用户的不同费用由多个账户付费,所以账户和用户之间是多对多的关系。

客户

客户是一个社会化的概念,一个自然人或一个法人就称之为一个客户,法人客户既可以是一个企业,也可以是与这个企业、集团相关的自然人客户,可以称之为一个集团客户组。客户可分为个人客户,企业客户。一个客户可以包含多个用户,客户通过唯一的客户标识来确定。客户描述一个客户的自然属性,客户可以和用户一一对应,也可以和用户一对多。即使不使用业务,也可能存在客户,因为客户是自然存在的。客户信息包含客户名,地址,邮政编码,性别,年龄,职业,证件号码等等信息,是自然存在的信息。

个人客户

在互联网系统中,一般是用户先注册,先有用户,然后补充客户身份信息。也就是客户身份信息是在运行过程中逐步完善的。在电信体系或银行体系,客户的主键通常是证件号(如身份证),将所有相同的证件视为同一客户。在互联网行业通常是将手机号识别为主键(注意:手机号有注销、更换等问题),这里面就有一个关键业务,既然相同证件的证件号或手机号会识别成一个客户,当相同的证件号或手机号进入系统时,系统是如何处理的?当然是合成一个了,这个过程叫做客户归并,即:将客户合成同一个客户号的过程,我们称为归并。归并是有风险的,所以需要一些鉴权手段来处理。

在互联网应用中,一个系统中的生命周期如下:


正常的互联网站点一般只有正常和禁入2种状态,银行业可能还会包含冻结和止付状态(以司法协查为例,某个客户被协查以致账户冻结,需要冻结该客户下所有资金账户,这些账户都被止付)。

注意在这个流程中没有销户的状态。这是为了支持历史业务的处理,客户一般不做销户。此外这个流程和支付账户的流程比较类似,这是为了方便在客户层面做账户控制。

存在问题,用户手机号更换带来的身份识别不能解决。

企业客户

企业客户相对个人客户来说比较复杂点,但三户模型仍然适用。企业客户是一个组织,其账户必然是组织授权内部人员去操作。但是这个操作人,同个人客户一样,只是系统的使用者,即用户。企业的资金比较大,并且有严格的业务流程,所以在系统使用上,一般是多个用户操作一个或多个资金账户。这种关系本身来说,也是一种授权关系,企业授权相应的用户来操作特定的资金账户,只不过为了管理方便,可以引入角色管理机制来实现。对于支付公司来说,企业客户通常都是发展商户过程中产生的。企业客户的识别同个人客户识别也是一样的,通过企业证件来统一识别。相同的企业证件号归并到同一个企业客户下面。建立企业客户的好处在于:
有些企业本身只开通了企业服务业务,而不开通商户服务
一个企业可以开通多个商户,企业客户是这些多个商户的统计口径

用户

用户是在产品基础上产生的实体。如果说一个客户使用了多个产品,那么一个客户就会对应好几个用户(即产品)。

个体用户

用户与资金账户之间我们可以抽象出一种授权关系,凡是授权用户,都可以操作资金账户,当然,这种授权包括客户自己的用户。用户的建立比较简单,一般自助注册后就可以生成用户实体了。

用户的生命周期如下:


如果用户想要销户,收到销户申请后,不能直接销户,客户是通过用户来进行资金账户的管理与操作的,所以,此时有个确认过程,要求各业务系统确认此用户下的所有账户是否可以销户,如果没有问题,先销资金账户,当用户下的所有资金账户都销户完毕,再销用户,用户销户完成后,会释放出此用户占用的资源,如注册手机号。

在互联网环境比较多的场景是用户更新手机号,用户更新手机号的时候涉及到的两个手机号都需要验证。需要注意的是, 更新手机号需要涉及到更新客户ID。

用户除了生命周期状态外,还有一个管理状态,比如锁定,从现实模型中来说,这个是不应该放在用户层面的而是放在账户层面上的,但互联网模式下,一个用户有多个资金账户,为了用户体验,把这些放在了用户层面上了,就如同支付密码放在用户层面上一样。

商户

商户是企业客户的一个业务影子,或是看成资金账户分组的一个手段。商户是客户一个外围业务,如果把它看成用户平级层面也是可以的,即:此商户所有业务产生的资金进入到一个分类资金账户里。不论怎么说,一个企业不论开多少个商户,每个商户又开通多少个资金账户,都改变不了资金账户的归属关系,它是现实客户这个实体的。

账户

账户的概念起源于金融业,只是一个客户存放资金的实体,目的是为选择的产品付费。一个客户可以拥有一个账户也可以拥有多个账户,账户上的资金可以为客户本人的用户付费,也可以为其他客户的用户付费,这种付费关系需要一个付费规则进行关联。

既然账户关系到付费规则,必然会引出账单的概念。一般来首先要生成用户账单,账单应该归属于用户。账单分为两级,客户账单和用户账单:
客户账单是根据用户账单按照规则进行简单的算术加和得到的。
用户账单可以进一步细分为账单项,账单项是为客户打印账单提供清晰明了的消费明细。

账单应当归属于用户,为客户提供的账单应当以产品为单元来生成账单,一般的消费习惯都是以产品为单元来付费;但同时也应该生成客户账单,如果一个客户选择了运营商的多个产品,那么客户如果需要一个所有产品的账单,运营商应当提供,同时集团客户需要一个集团所有客户的消费明细,也需要有一个集团客户账单。

用户和账户的映射关系,主要就是销账规则。销账流程中处理模型应当也是按照用户的账单来销账,而不是按照客户账单,客户的账单应当只是用户账单的简单算术运算的得到的账单,只提供打印。

客户和账户应当有一个归属的对应规则,该规则应当是一种归属关系,个人账户应该归属于个人客户,集团账户应当归属于集团客户。但这只是一种归属关系,而没有付费关系,账户可以跨客户为几个用户付费,也可以为单个用户账单的某个账目付费。

产品在市场提供时难免会遇到,产品的某项子功能的交叉优惠,这种交叉优惠,应当打包成为一个产品。在具体的系统模型中的体现就是增加一个用户,并赋予一定的资费,同时指定一个账户来为其销账,就统一了整个模型。

账户的生命周期:


账户建模

在支付系统中,账户的建模,主要是从如下几个方面来考虑:
交易的需求,比如检查账户是否被锁定、余额是否足够、是否有效等。
记账的需求,按照公司会计需求记录账户上的所有行为,包括支出、充值、转账等。
对账的需求,包括和支付渠道、商户、个人的对账需求,核对交易和账户余额是否正确。
风控的需求,如反洗钱、反欺诈等,都需要依赖于账户体系来提供核心数据。
信用的需求,对用户、资产、商户等主体进行信用评估时,也需要依赖账户体系来提供的核心数据。

这五个需求,按照其设计的优先级,也是从支付、记账、对账、风控来进行。支付系统根据其发展所处的阶段,逐步将新增需求纳入设计中。根据业务需要,可以设置多种账户,如支付账户、预付卡账户、代扣账户、零钱账户、结算账户等。一般来说电商系统中涉及的账户类型有:
虚拟币账号:用户和使用虚拟币的商户都需要建立虚拟币账户。
代扣账号: 用来支持订阅类型的定期代扣;
零钱账号:即电商的内部账号,用户、商户、清算单位需要建立零钱账户;
第三方支付账号:用户在第三方支付机构建立的账户。
银行卡账号:用户的银行卡信息,每个卡对应一个账户。
结算账号:用来支持和第三方支付公司、银行进行结算用。 第三方支付需要为每个商户号建立结算账号;银行需要为借记卡、贷记卡分别建立结算账号(有必要吗?银行卡直连时使用)。
代扣代缴账户:用来支持代扣税款业务。

注意,有些第三方信息是不能保存的,如信用卡的CV号等。

三层身份模型

从使用层面,三户模型更加适合交易类网站,三层身份模型可能更加适合社交性质的网站。三层身份模型将用户分层三个层次,分别为:账户标识符,登录标识符和公开标识符。翻译成大白话可以是:账户ID、登陆账号、昵称。


账户标识符(DB Key)

从软件工程的角度,需要在数据库中存在一个用户标识(key)来记录每个用户的记录。这个用户标识可能会被用在Cookies或者URL中,这个账户标识符必须是永久的、唯一的。通常是由系统自动生成的一个ID,这个ID不受用户的控制,从用户角度这个标识符应该是不不可见或至少是惰性的,这个标识符不应该与一些公共名称,比如手机号或邮箱。

登录标识符(会话认证)

在创建与账户标识符关联的有效会话(Session)时,登陆标识符时必须要的,他们用来从服务中获取授权。通常由唯一的账号/密码对表示(这里的账号可以是邮箱或手机号,也可以是用户选择的用户名)。注意,账户/密码并不是一定需要存在的,笔筒通过oAuth形式的授权登陆(类似微信登陆、微博登陆等)。将登陆标识符与账户标识符分开,可以使得用户更加容易的改变登陆方式。由于账户标识符不需要更改,因此可以避免更换登陆方式或登陆账号带来的数据迁移问题。最重要的是,可以提供多个不同登陆标识符附加到单个账户上,从而可以允许服务聚合从多个身份供应商收集的信息(如授权获得微信或微博的个人信息)。

公开标识符(社会标识)

与账户标识符和登陆标识符不同,公开标识符标识用户希望如何被服务商的其他用户感知。在线用户的公共标识符通常是复合对象:照片,昵称,可能还有年龄,性别和位置。它为任何观众提供了足够的信息,可以快速解读个人背景。公共标识符通常链接到详细的用户配置文件,其中可以进一步识别身份。公开标识符在某些情况下需要唯一(比如类似微博的@功能),有时是不需要唯一的(如微信的昵称)。