ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

扩展.Net Core Identity Server 授权方式,实现 手机号+ 验证码 登录

2022-05-07 12:32:20  阅读:350  来源: 互联网

标签:Core code return string Server phoneNumber context Net public


背景

国内来讲,注册/登录流程都是尽可能的简单,注册流程复杂,容易流失客户。手机号 + 短信验证码的方式非常普遍;但是框架默认并没有类似的功能,需要我们自己进行扩展。

 

思路

  1. 验证登录手机号为注册用户,且验证码正确;验证通过后,去 Identity Server 获取Token,然后返回客户端。
  2. 扩展 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     }
5         
View Code

 

实现 IExtensionGrantValidator

主要实现对手机号以及短信验证码的校验

 

  1  public class PhoneCodeGrantValidator : IExtensionGrantValidator, ITransientDependency
  2     {
  3         private readonly IOptions<IdentityOptions> _identityOptions;
  4         private readonly IAccountRepository _accountRepository;
  5         private readonly IdentityUserManager _identityUserManager;
  6         private readonly AccountTokenManager _accountTokenManager;
  7 
  8         public string GrantType => IdentityGrantTypes.PhoneCode;
  9 
 10         public PhoneCodeGrantValidator(
 11             IOptions<IdentityOptions> identityOptions,
 12             IAccountRepository accountRepository,
 13             IdentityUserManager identityUserManager,
 14             AccountTokenManager accountTokenManager)
 15         {
 16             _identityOptions = identityOptions;
 17             _accountRepository = accountRepository;
 18             _identityUserManager = identityUserManager;
 19             _accountTokenManager = accountTokenManager;
 20         }
 21 
 22         public async Task ValidateAsync(ExtensionGrantValidationContext context)
 23         {
 24             await _identityOptions.SetAsync();
 25 
 26             var phoneNumber = context.Request.Raw.Get("phoneNumber");
 27             var code = context.Request.Raw.Get("code");
 28 
 29             var validateParamsResult = ValidateRequestParams(phoneNumber, code);
 30             if (!validateParamsResult.IsNullOrWhiteSpace())
 31             {
 32                 SetContextError(validateParamsResult, context);
 33                 return;
 34             }
 35             
 36             var identityUser = await _accountRepository.FindByConfirmedPhoneAsync(phoneNumber);
 37             if (identityUser == null)
 38             {
 39                 SetContextError("无效的手机号", context);
 40                 return;
 41             }
 42             
 43             if (await _identityUserManager.IsLockedOutAsync(identityUser))
 44             {
 45                 SetContextError("账户已锁定", context);
 46                 return;
 47             }
 48 
 49             var validateCodeResult = await ValidateCodeLoginAsync(phoneNumber, code);
 50             if (!validateCodeResult.IsNullOrWhiteSpace())
 51             {
 52                 await _identityUserManager.AccessFailedAsync(identityUser);
 53                 SetContextError(validateCodeResult, context);
 54                 return;
 55             }
 56 
 57             var claims = new List<Claim>
 58             {
 59                 new("phoneNumber", phoneNumber)
 60             };
 61 
 62             if (identityUser.TenantId.HasValue)
 63             {
 64                 claims.Add(new Claim(AbpClaimTypes.TenantId, identityUser.TenantId?.ToString()));
 65             }
 66 
 67             claims.AddRange(identityUser.Claims.Select(
 68                 item => new Claim(item.ClaimType, item.ClaimValue)));
 69 
 70             context.Result = new GrantValidationResult(identityUser.Id.ToString(), GrantType, claims);
 71         }
 72 
 73         public async Task<string> ValidateCodeLoginAsync(string phoneNumber, string code)
 74         {
 75             var isValidCode = await _accountTokenManager.VerifySignInCodeAsync(phoneNumber, code);
 76 
 77             return !isValidCode ? "无效的手机号或验证码" : string.Empty;
 78         }
 79 
 80         private static string ValidateRequestParams(
 81             string phoneNumber, string code)
 82         {
 83             if (string.IsNullOrWhiteSpace(phoneNumber))
 84             {
 85                 return "手机号不能为空";
 86             }
 87 
 88             if (string.IsNullOrWhiteSpace(code))
 89             {
 90                 return "验证码不能为空";
 91             }
 92 
 93             return string.Empty;
 94         }
 95 
 96         private static void SetContextError(
 97             string errorMessage, ExtensionGrantValidationContext context)
 98         {
 99             context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant)
100             {
101                 ErrorDescription = errorMessage
102             };
103         }
104     }
View Code

 

注册扩展服务

1   public override void PreConfigureServices(ServiceConfigurationContext context)
2     {
3         PreConfigure<IIdentityServerBuilder>(builder =>
4         {
5             builder.AddExtensionGrantValidator<PhoneCodeGrantValidator>();
6         });
7     }

 

简单验证

至此,扩展方式的核心工作已经准备完成,可以通过 postman 进行简单的实验。

 

 

非扩展授权方式

此方式也比较简单,校验手机号以及验证码的主体逻辑一致,只需要验证用户之后,通过 httpClient 去 IdentityServer 获取token,然后返回客户端即可。

其他:为了更好的安全,在登录失败后需要显式的标记登录失败,配合 Identity 的一些策略,可以对一段时间内登录失败次数过多的账户,进行锁定,防止用户信息泄露。

 

 

结尾

近期已经从公司离职了。近期也思考了很多,大城市与二线城市在做事风格上确实差别比较大;也有很多令人唏嘘的事情,改天总结一下。

标签:Core,code,return,string,Server,phoneNumber,context,Net,public
来源: https://www.cnblogs.com/qiu-gu/p/16194195.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有