- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
@ 。
双因素认证(Two-Factor Authentication,简称 2FA)是使用两个或多个因素的任意组合来验证用户身份,例如用户提供密码后,还要提供短消息发送的验证码,以证明用户确实拥有该手机.
国内大多数网站在登录屏正常登录后,检查是否有必要进行二次验证,如果有必要则进入二阶段验证屏,如下图:
接下来就来实践这个小项目 。
本示例基于之前的博文内容,你需要登录并绑定正确的手机号,才能使用双因素认证。示例代码已经放在了GitHub上: Github:matoapp-samples 。
查看Abp源码,Abp帮我们定义了几个Setting,用于配置双因素认证的相关功能。确保在数据库中将Abp.Zero.UserManagement.TwoFactorLogin.IsEnabled打开.
public static class TwoFactorLogin
{
/// <summary>
/// "Abp.Zero.UserManagement.TwoFactorLogin.IsEnabled".
/// </summary>
public const string IsEnabled = "Abp.Zero.UserManagement.TwoFactorLogin.IsEnabled";
/// <summary>
/// "Abp.Zero.UserManagement.TwoFactorLogin.IsEmailProviderEnabled".
/// </summary>
public const string IsEmailProviderEnabled = "Abp.Zero.UserManagement.TwoFactorLogin.IsEmailProviderEnabled";
/// <summary>
/// "Abp.Zero.UserManagement.TwoFactorLogin.IsSmsProviderEnabled".
/// </summary>
public const string IsSmsProviderEnabled = "Abp.Zero.UserManagement.TwoFactorLogin.IsSmsProviderEnabled";
...
}
在AbpUserManager的GetValidTwoFactorProvidersAsync方法中 。
Abp.Zero.UserManagement.TwoFactorLogin.IsSmsProviderEnabled开启后将添加“Phone”到Provider中,将启用短信验证方式.
Abp.Zero.UserManagement.TwoFactorLogin.IsEmailProviderEnabled开启后将添加“Email”到Provider中,将启用邮箱验证方式.
var isEmailProviderEnabled = await IsTrueAsync(
AbpZeroSettingNames.UserManagement.TwoFactorLogin.IsEmailProviderEnabled,
user.TenantId
);
if (provider == "Email" && !isEmailProviderEnabled)
{
continue;
}
var isSmsProviderEnabled = await IsTrueAsync(
AbpZeroSettingNames.UserManagement.TwoFactorLogin.IsSmsProviderEnabled,
user.TenantId
);
if (provider == "Phone" && !isSmsProviderEnabled)
{
continue;
}
在迁移中添加双因素认证的配置项 。
//双因素认证
AddSettingIfNotExists(AbpZeroSettingNames.UserManagement.TwoFactorLogin.IsEnabled, "true", tenantId);
AddSettingIfNotExists(AbpZeroSettingNames.UserManagement.TwoFactorLogin.IsSmsProviderEnabled, "true", tenantId);
AddSettingIfNotExists(AbpZeroSettingNames.UserManagement.TwoFactorLogin.IsEmailProviderEnabled, "true", tenantId);
将默认User的IsTwoFactorEnabled字段设为true 。
public User()
{
this.IsTwoFactorEnabled= true;
}
使用 AbpBoilerplate.Sms 作为短信服务库.
之前定义了DomainService接口,已经实现了验证码的发送、验证码校验、解绑手机号、绑定手机号 。
这4个功能,通过定义用途(purpose)字段以校验区分短信模板 。
public interface ICaptchaManager
{
Task BindAsync(string token);
Task UnbindAsync(string token);
Task SendCaptchaAsync(long userId, string phoneNumber, string purpose);
Task<bool> VerifyCaptchaAsync(string token, string purpose = "IDENTITY_VERIFICATION");
}
添加一个用于双因素认证的purpose,在CaptchaPurpose枚举类型中添加 TWO_FACTOR_AUTHORIZATION 。
public const string TWO_FACTOR_AUTHORIZATION = "TWO_FACTOR_AUTHORIZATION";
在SMS服务商管理端后台申请一个短信模板,用于双因素认证.
打开短信验证码的领域服务类SmsCaptchaManager, 添加 TWO_FACTOR_AUTHORIZATION 对应短信模板的编号 。
public async Task SendCaptchaAsync(long userId, string phoneNumber, string purpose)
{
var captcha = CommonHelper.GetRandomCaptchaNumber();
var model = new SendSmsRequest();
model.PhoneNumbers = new string[] { phoneNumber };
model.SignName = "MatoApp";
model.TemplateCode = purpose switch
{
CaptchaPurpose.BIND_PHONENUMBER => "SMS_255330989",
CaptchaPurpose.UNBIND_PHONENUMBER => "SMS_255330923",
CaptchaPurpose.LOGIN => "SMS_255330901",
CaptchaPurpose.IDENTITY_VERIFICATION => "SMS_255330974"
CaptchaPurpose.TWO_FACTOR_AUTHORIZATION => "SMS_1587660" //添加双因素认证对应短信模板的编号
};
...
}
创建双因素认证领域服务类TwoFactorAuthorizationManager.
创建方法IsTwoFactorAuthRequiredAsync,返回登录用户是否需要双因素认证,若未开启TwoFactorLogin.IsEnabled、用户未开启双因素认证,或没有添加验证提供者,则跳过双因素认证.
public async Task<bool> IsTwoFactorAuthRequiredAsync(AbpLoginResult<Tenant, User> loginResult)
{
if (!await settingManager.GetSettingValueAsync<bool>(AbpZeroSettingNames.UserManagement.TwoFactorLogin.IsEnabled))
{
return false;
}
if (!loginResult.User.IsTwoFactorEnabled)
{
return false;
}
if ((await _userManager.GetValidTwoFactorProvidersAsync(loginResult.User)).Count <= 0)
{
return false;
}
return true;
}
创建TwoFactorAuthenticateAsync,此方法根据回传的provider和token值校验用户是否通过双因素认证.
public async Task TwoFactorAuthenticateAsync(User user, string token, string provider)
{
if (provider == "Email")
{
var isValidate = await emailCaptchaManager.VerifyCaptchaAsync(token, CaptchaPurpose.TWO_FACTOR_AUTHORIZATION);
if (!isValidate)
{
throw new UserFriendlyException("验证码错误");
}
}
else if (provider == "Phone")
{
var isValidate = await smsCaptchaManager.VerifyCaptchaAsync(token, CaptchaPurpose.TWO_FACTOR_AUTHORIZATION);
if (!isValidate)
{
throw new UserFriendlyException("验证码错误");
}
}
else
{
throw new UserFriendlyException("验证码提供者错误");
}
}
创建SendCaptchaAsync,此方用于发送验证码.
public async Task SendCaptchaAsync(long userId, string Provider)
{
var user = await _userManager.FindByIdAsync(userId.ToString());
if (user == null)
{
throw new UserFriendlyException("找不到用户");
}
if (Provider == "Email")
{
if (!user.IsEmailConfirmed)
{
throw new UserFriendlyException("未绑定邮箱");
}
await emailCaptchaManager.SendCaptchaAsync(user.Id, user.EmailAddress, CaptchaPurpose.TWO_FACTOR_AUTHORIZATION);
}
else if (Provider == "Phone")
{
if (!user.IsPhoneNumberConfirmed)
{
throw new UserFriendlyException("未绑定手机号");
}
await smsCaptchaManager.SendCaptchaAsync(user.Id, user.PhoneNumber, CaptchaPurpose.TWO_FACTOR_AUTHORIZATION);
}
else
{
throw new UserFriendlyException("验证提供者错误");
}
}
接下来将双因素认证逻辑添加到登录流程中.
在web.core项目中, 添加类SendTwoFactorAuthenticateCaptchaModel,发送验证码时将一阶段返回的userId和选择验证方式的provider传入 。
public class SendTwoFactorAuthenticateCaptchaModel
{
[Range(1, long.MaxValue)]
public long UserId { get; set; }
[Required]
public string Provider { get; set; }
}
将验证码Token,和验证码提供者Provider的定义添加到AuthenticateModel中 。
public string TwoFactorAuthenticationToken { get; set; }
public string TwoFactorAuthenticationProvider { get; set; }
将提供者列表TwoFactorAuthenticationProviders,和是否需要双因素认证RequiresTwoFactorAuthenticate的定义添加到AuthenticateResultModel中 。
public bool RequiresTwoFactorAuthenticate { get; set; }
public IList<string> TwoFactorAuthenticationProviders { get; set; }
打开TokenAuthController,注入UserManager和TwoFactorAuthorizationManager服务对象 。
添加终节点SendTwoFactorAuthenticateCaptcha,用于前端调用发送验证码 。
[HttpPost]
public async Task SendTwoFactorAuthenticateCaptcha([FromBody] SendTwoFactorAuthenticateCaptchaModel model)
{
await twoFactorAuthorizationManager.SendCaptchaAsync(model.UserId, model.Provider);
}
改写Authenticate方法如下:
[HttpPost]
public async Task<AuthenticateResultModel> Authenticate([FromBody] AuthenticateModel model)
{
//用户名密码校验
var loginResult = await GetLoginResultAsync(
model.UserNameOrEmailAddress,
model.Password,
GetTenancyNameOrNull()
);
await userManager.InitializeOptionsAsync(loginResult.Tenant?.Id);
//判断是否需要双因素认证
if (await twoFactorAuthorizationManager.IsTwoFactorAuthRequiredAsync(loginResult))
{
//判断是否一阶段
if (string.IsNullOrEmpty(model.TwoFactorAuthenticationToken))
{
//一阶登录完成,返回结果,等待二阶段登录
return new AuthenticateResultModel
{
RequiresTwoFactorAuthenticate = true,
UserId = loginResult.User.Id,
TwoFactorAuthenticationProviders = await userManager.GetValidTwoFactorProvidersAsync(loginResult.User),
};
}
//二阶段,双因素认证校验
else
{
await twoFactorAuthorizationManager.TwoFactorAuthenticateAsync(loginResult.User, model.TwoFactorAuthenticationToken, model.TwoFactorAuthenticationProvider);
}
}
//二阶段完成,返回最终登录结果
var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
return new AuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
UserId = loginResult.User.Id,
};
}
至此,双因素认证的后端逻辑已经完成,接下来我们将补充“记住”功能,实现一段时间内免验证.
最后此篇关于用Abp实现双因素认证(Two-FactorAuthentication,2FA)登录(一):认证模块的文章就讲到这里了,如果你想了解更多关于用Abp实现双因素认证(Two-FactorAuthentication,2FA)登录(一):认证模块的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
The proper divisors of a positive integer, n, are all the positive integers that divide n evenly oth
我有这个命令行 $ sudo find /etc/grub.d | sort | tail -n 1 | xargs sudo cat | wc 我想用一个 sudo 命令执行 $ sudo --so
选项大小策略和拉伸(stretch)因子如何影响小部件的大小? 下图显示了三个不同排列的窗口的预览。对于所有三个窗口 (W1-W3),右侧的小部件是一个 QFrame 小部件,其水平和垂直大小策略设置
每次当我必须重新编码一组变量时,我都会想到 SPSS 重新编码功能。我必须承认这很简单。有一个类似的recode函数在 car包,它可以解决问题,但让我们假设我想用 factor 完成任务. 我有 d
这个问题在这里已经有了答案: Template issue causes linker error (C++) [duplicate] (6 个答案) 关闭 9 年前。 我的问题查了没用所以特地来问
我想使用 Eigen 来计算稀疏矩阵的 cholesky 分解。但是,结果不正确,我找不到原因。我如何获得正确答案? Eigen 中是否实现了特殊例程,利用稀疏矩阵的结构来提高性能(例如,对于下例中的
我正在尝试使 angularjs 应用程序在配置( http://12factor.net/config )方面符合 12 因素。 它应该取决于环境,我不应该看到 development 字样, te
我在我的项目中使用 Soil,我在我的包含目录中添加了 soil,在我的预编译头文件中我包含了“Soil.h”。对于我预编译头中的库,我添加了这个: #pragma comment(lib,"SOIL
在我的 Web 应用程序中,我将所有最终用户的日期信息以 UTC 格式存储在数据库中,在向他们显示之前,只需将 UTC 日期转换为他们选择的时区。 我正在使用此方法将本地时间转换为 UTC 时间(在存
我的申请是 Piwik Server从放置在数百个网站上的跟踪代码接收传入的跟踪数据。当这些跟踪请求进入时,大部分工作负载是每秒向数据库写入数百次。我使用的是带有 JDBC 和 Hibernate 的
我有一个非常简单的 GWT 应用程序,它收集一些数据并在用户单击“提交”时提供确认对话框。我创建了一个 com.google.gwt.user.client.ui.DialogBox,填充它,然后调用
我正在使用 Delphi(2009 年,没关系)和 IBX,并且我正在尝试执行简单的代码: TestSQL.ExecQuery; 在此代码之前,我已检查(也可以在调试器监视中看到)TestSQL.Tr
许多线性代数例程都将常量(例如 alpha 和 beta)作为参数。例如cublas?GEMM执行以下操作: C := alpha*op( A )op( B ) + betaC 假设我将 beta 设
我是一名优秀的程序员,十分优秀!