三个关键点:1. TokenProvider,它用来生成验证码。2. 信息格式。3. 信息发送服务。
3. 在身份验证管道中加入双因子验证中间件:
两个中间件前者用于处理二次验证,后者用于记住登录状态,下次访问系统时自动登录。
4. 添加验证码发送方式选择以及验证码填写页面及相应的Action方法(代码略)。
5. 在数据库中将演示用的用户信息改为启用二次验证(注:模板代码中有用于管理个人信息的功能,此处省略了实现,直接通过修改数据数据的方式开启用户的双因子验证、添加电话号码等):
6. 运行结果:
登录后需要选择验证码发送方式:
选择后点击提交按钮,页面调整到验证页面的同时,指定的文件中生成了需要的验证码:
填写验证码后点击提交按钮,则登录成功:
注:双因子验证也可以应用到第三方账户的登录方式上,双因子验证只与用户有关与身份验证方式无关。
针对验证码生成与验证的说明 对于双因子验证来说,它实际上就是在普通验证或第三方账户验证的基础上增加了验证码的发送和验证两个环节,那么对于验证码这个主体Identity是如何来维护的呢?
在上面的介绍中,有一个环节就是需要通过对UserManager进行配置以支持双因子验证的消息发送、消息生成等等:
根据这个代码看来XXXTokenProvider是专门用来维护验证码的,而XXXService是用来发送的,所以这里将对TokenProvider进行说明,了解验证码是如何维护的:
上图是TokenProvider相关的一个简单类图,从类图中可以看出TokenProvider实际上是实现了一个名为IUserTokenProvider的接口,该接口中有4个方法,它们的作用分别是:
● GenerateAsync:根据UserManager以及User信息来生成一个令牌(Token)。
● IsValidProviderForUserAsync:判断这个Token提供器对这个用户是否是有效的(如果使用短信验证,但是该用户没有设置手机号,那么就是无效的)。
● NotifyAsync:当Token生成后调用该方法通知用户,如短信或邮件通知。
● ValidateAsync:用于验证Token是否有效。
而TotpSecutityStampBasedTokenProvider是一个实现了IUserTokenProvider接口的,通过用户安全戳生成验证码的生成器:
从代码中可以看到该算法是基于rfc6238(TOTP: Time-Based One-Time Password Algorithm,基于时间的一次性密码算法) https://tools.ietf.org/html/rfc6238,然后通过用户的安全戳以及GetUserModifierAsync方法生成特定的信息熵来完成密码加密,关于信息熵可参考:https://www.zhihu.com/question/22178202,上面将生成后的令牌执行ToString("D6")是将其转换为一个6位数字的字符串。
而Token的验证方式和生成差不多都是通过用户安全戳和信息熵来验证提交的验证码(它实际上是一种hash算法):