1.概述
登录功能包括两个主要的组成部分:认证和登录
认证 的功能依赖于具体的方案选择,登录 的功能则依赖于具体的业务需求。
本文的主要目标,是识别出影响登录功能的设计因素。
设计目标
总的来说,登录的‘意义’可以用下图表示:
- 登录的目的是为了在后续的业务请求中,能够识别用户身份;
- 认证的方式本身是多种多样的;
- 用户的‘身份’在不同的系统中,表现形式是多种多样的;
所以登录设计应该达到以下几个目标:
- 能够支持多种认证手段;
- 能够通过很容易的扩展,支持新的认证手段;
- 对业务处理透明
2.相关因素
2.1.通用因素
开始‘认证登录设计’之前,肯定要和用户进行沟通,从而确定‘登录’这件事的影响范围,有多少事情要做。一般的,我们会问客户几个问题:
- 要不要用手机号登录啊?
- 用微信小程序吗?
- 新用户登录后怎么办啊?
- 需要客服代理用户身份吗?
- 有几种用户啊?比方说 客户/销售/财务 各自需要各自的登录入口
- 扫码登录了解一下?
...
归纳一下,是以下4个方面的信息,称之为"登录上下文":
- 登录渠道
- 登录方式
- 登录信息
- 登录目标
2.1.1.登录渠道
通过哪个客户端,哪个界面登录的。
例如,电商网站,卖家登录后台,和买家登录账号,都是通过PC端网页登录的,在实现中, 一个使用类似 https://...webservice/merchantLogin/ 这样的接口登录, 另外一个通过 https://...webService/userLogin/ 这样的接口来登录。
在移动端,买家还可以通过一个APP登录,通过调用 https://...mobileService/login/ 来登录
我们说,它们的登录渠道不同。
不同的登录渠道,通过以下信息来识别:
- 客户端。(从哪里发起的请求,例如通过header中的字段来标识 web 和 APP的不同场景)
- service bean。(是哪个服务接收的登录请求。可以使用controller的类,bean name等来标识,例如上面的webService和mobileService)
- service name。(使用哪个具体的接口处理登录请求。一般可以映射到方法名,例如上面的 merchantLogin和userLogin)
2.1.2.登录方式
使用什么方法登录的
例如,使用微信登录;使用密码登录;使用扫脸登录...
2.1.3.登录信息
使用不同的登录方式,需要提供相应的各种信息
例如,使用微信登录,需要前台提供code;使用密码登录,需要提供ID+密码;使用扫脸登录,需要提供匹配因子...
2.1.4.登录目标
登录完成后,我以一个什么样的身份进行后续的操作。
例如,前面电商系统,不同的登录方式成功后,应该能够确定‘我’是一个买家还是一个卖家。
2.2.业务因素
登录的结果,常见的有这么几种
- 认证通过,身份确定
- 认证通过,但是没有身份
- 认证失败
“认证通过,身份也确定” 这种情况通常会问客户的问题是“登录成功以后,是去’我的‘页面啊,还是去’首页‘啊? " 不同的业务系统有不同的要求,例如一个任务管理系统,登录以后一般会要求去到‘我的’页面,查看用户的dashboard,处理‘待处理任务’。而一个交易系统,登录之后一般会希望去到‘交易市场’类的页面。设计时,这里要允许业务模块自由的处理登录成功后的页面流向。
“认证通过,但是没有身份” 这种情况,一般需要引导注册或者自动创建用户。
例如,使用微信登录,用户授权后,通过后台验证此用户有效,但是用它包含的open-id却不能找到对应的买家(或者其他什么业务所需的角色),这个时候怎么办?问用户!这是个业务层面的问题。
有3种可能:
- 认为登录失败。 例如某内部工作流系统,微信登录后发现没有对应的‘员工’,那么就告诉他登录失败,需要管理员先为其创建员工,指定部门和权限,然后绑定微信以后,才能使用微信快速登录。
- 引导注册。 例如某众包系统,微信登录后需要补充一些必要的信息,才能继续进行后续操作,此时需要将页面流导向注册页面。
- 自动创建。例如某社区类应用,微信登录后就可以(金银铜铁锡之)‘锡牌’级别用户身份进行浏览。
这3种可能需要根据业务来确定,甚至还有其他可能,所以我们的设计应该识别出这是一个“业务”选择,在设计中,让业务能够介入处理。设计目标是“让业务介入”而不是“如何引导注册”这样的具体实现。
“认证失败” 这种情况指认证本身失败。例如,密码输错,令牌过期,扫码失败等等。这时一般的处理方式时告知客户登录失败,然后由业务模块决定下一步是继续重试啊,还是换个登录方式啊--总之由业务决定。
从上面的描述可以看到,业务模块要能够介入:
- 登录后的处理
- 登录处理完成后的页面流向
框架部分和业务部分的关系是这个样子:
3.概念
3.1.sec-user (安全用户)
使用不同的登录方式,获得的被认证的身份类型是多种多样的。为了达到“业务透明”的设计目标,需要一个统一的逻辑概念来表达‘用户’。
在我们团队使用的DaaS的实现中,引入了“sec-user”这个概念。它表示“确认过的安全的用户”。
不同方式登录进来的各种身份,都会关联到一个 sec-user, 然后在业务中,就可以统一的使用sec-user来表示用户。
“用户”这个概念,并不局限于使用终端的‘人’。在物联网中,任何需要具备身份信息和系统交互的实体,都是‘用户’。例如一个买家,一个门禁,一辆卡车,一个托盘...
3.2.identification handler(认证器)
为了达到设计目标的“ 支持多种认证手段;可以扩展 ”,引入“identification handler"(以下简称ID-handler)的概念。 它的任务是针对不同的 登录方式 ,完成对 登录信息 的认证功能。
例如”微信小程序认证器”,“短信验证码认证器”,“刷脸认证器”
目前我们团队使用的DaaS支持以下几种ID-handler
- ID/email/手机号+密码认证
用户需要提供用户ID,或者手机号,以及登录密码。登录结果不支持“认证通过但是没有身份”这种情况;
- 手机号+验证码认证
用户需要先获取验证码,然后用手机号和验证码一起完成认证
- 微信小程序认证
使用微信官方的小程序认证规范完成认证,需要客户端获取code
- 企业微信认证
使用微信官方的企业微信认证,需要客户端获取企业微信code
- 公私钥对认证
客户端使用私钥提供一个签名,服务器使用其对应公钥进行验证。需要客户端首先和服务器协商签名,并且通过其他渠道预先分发秘钥。大部分用于物联网设备
- 微信扫码认证
客户端显示一个微信二维码,用户使用手机微信扫码,登录客户端;
- QR-ID/RFID
在特定场景下,只需确定用户ID,无需认证。例如物流平台中签到,核单等
- email+验证码认证
用户需要先通过邮箱获取验证码,然后用验证码登录
- 更多认证方式...
3.2.business handler (业务处理器)
为了让业务模块能够介入 登录目标 在不同 登录渠道 的处理,我们以business handler(以下简称biz-handler)这个概念来代指业务处理逻辑相关部分,以之描述登录功能与具体业务的边界和交互。
3.3.小结
登录部分的主要流程如下:
4. High level Design
4.1.登录部分
从上面的描述中,可以知道,设计的核心概念就是
- 选取正确的 ID-handler,进行认证
- 保存身份信息到统一的用户模型(sec-user)
- 选取正确的biz-handler,处理业务因素
基于我们团队使用的DaaS,一个high-level-design如下图所示:
4.2.鉴权部分
登录的目的是为了 在后续的业务处理中,能够识别用户身份 (见‘概述’),所以这里将基于DaaS实现的鉴权部分也简单描述一下。
- DaaS中,每个请求都会做鉴权。最终会调用到对应的bean的 checkAccess 方法,所以此图以checkAccess作为入口
- token key的优先级是:先header,再cookie,最后 session ID
- 匿名用户也拥有自己的‘身份’,因此也有对应的一套token
- OOTB的鉴权结果只有两种:accessOK 或者 accessFail。(在DaaS中提供了简单的方法来重载checkAccess,鉴权结果可以根据需要定制)
5.实现上的考虑
此设计并未限定实现方式,它只是明确了职责划分和业务依赖。可以使用普通的代码,一段一段的实现,也可以使用‘响应式编程’的风格来处理;同步事件既可以是直接方法调用,也可以是阻塞式消息响应。
重要的部分在于:
- 框架和业务的职责要划分清楚。 大部分系统不易维护的原因,在于职责边界混淆,当发生变更的时候才发现牵一发而动全身;
- 边界事件要定义良好。在模块之间,子系统之间,要提供完备的,稳定的事件定义。不要使用任何“潜规则”。通常来说,每种事件都会有一个长期维护的事件代码,当事件细节发生变化时,通过新增事件代码来支持,而不是修改事件定义。‘事件代码’是一个“你懂得”的“千言万语汇成一句话”。DaaS的实现中,就通过ChangeRequest中的bindedEventType来标定每种事件,保证了良好的扩展性。
- 注意控制权的合理变化。登录算是一个典型的“基本上一样”,但是又“每次都要改”的功能。识别出它的通用因素和业务因素,就能明白,控制登录的,不是一个单一模块,该交给框架的时候要交给框架,该交给外部服务的时候就交给外部服务,最后的控制权还要交回给业务。通常对复杂的业务过程,例如工作流,大家很容易就处理了控制权的转移,因为在认知上就准备好了。而登录一般表现为很小的一个功能,通常开发人员都是一气呵成,忽略了它在概念上,是多个层次,多个领域合作的。