扩展.Net Core Identity Server 授权方式,实现 手机号+ 验证码 登录
背景
国内来讲,注册/登录流程都是尽可能的简单,注册流程复杂,容易流失客户。
手机号 + 短信验证码的方式非常普遍;但是框架默认并没有类似的功能,需要我们自己进行扩展。
思路
- 验证登录手机号为注册用户,且验证码正确;验证通过后,去 Identity Server 获取Token,然后返回客户端。
- 扩展 Identity 的授权方式,类似于 Authorization code;
关于gtant type 可以参考 Grant Types — IdentityServer4 1.0.0 documentation (identityserver4test.readthedocs.io)
不过扩由于Identity Server 要收费,以及Abp 6.0 要集成 OpenIdDict;扩展 Grant Type 的方式,可以适用于当前,后续根据需要进行调整。
定义 GrantTypes
1 public class IdentityGrantTypes 2 { 3 public const string PhoneCode = "phone_code"; 4 }
实现 IExtensionGrantValidator
主要实现对手机号以及短信验证码的校验
1 public async Task ValidateAsync(ExtensionGrantValidationContext context) 2 { 3 await _identityOptions.SetAsync(); 4 5 var phoneNumber = context.Request.Raw.Get("phoneNumber"); 6 var code = context.Request.Raw.Get("code"); 7 8 var validateParamsResult = ValidateRequestParams(phoneNumber, code); 9 if (!validateParamsResult.IsNullOrWhiteSpace()) 10 { 11 SetContextError(validateParamsResult, context); 12 return; 13 } 14 15 var identityUser = await _accountRepository.FindByConfirmedPhoneAsync(phoneNumber); 16 if (identityUser == null) 17 { 18 SetContextError("无效的手机号", context); 19 return; 20 } 21 22 if (await _identityUserManager.IsLockedOutAsync(identityUser)) 23 { 24 SetContextError("账户已锁定", context); 25 return; 26 } 27 28 var validateCodeResult = await ValidateCodeLoginAsync(phoneNumber, code); 29 if (!validateCodeResult.IsNullOrWhiteSpace()) 30 { 31 await _identityUserManager.AccessFailedAsync(identityUser); 32 SetContextError(validateCodeResult, context); 33 return; 34 } 35 36 var claims = new List37 { 38 new("phoneNumber", phoneNumber) 39 }; 40 41 if (identityUser.TenantId.HasValue) 42 { 43 claims.Add(new Claim(AbpClaimTypes.TenantId, identityUser.TenantId?.ToString())); 44 } 45 46 claims.AddRange(identityUser.Claims.Select( 47 item => new Claim(item.ClaimType, item.ClaimValue))); 48 49 context.Result = new GrantValidationResult(identityUser.Id.ToString(), GrantType, claims); 50 }
注册扩展服务
1 public override void PreConfigureServices(ServiceConfigurationContext context) 2 { 3 PreConfigure(builder => 4 { 5 builder.AddExtensionGrantValidator (); 6 }); 7 }
简单验证
至此,扩展方式的核心工作已经准备完成,可以通过 postman 进行简单的实验。
非扩展授权方式
此方式也比较简单,校验手机号以及验证码的主体逻辑一致,只需要验证用户之后,
通过 httpClient 去 IdentityServer 获取token,然后返回客户端即可。
其他:为了更好的安全,在登录失败后需要显式的标记登录失败,配合 Identity 的一些策略,
可以对一段时间内登录失败次数过多的账户,进行锁定,防止用户信息泄露。