扩展.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 }
5
实现 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 }
注册扩展服务
1 public override void PreConfigureServices(ServiceConfigurationContext context)
2 {
3 PreConfigure<IIdentityServerBuilder>(builder =>
4 {
5 builder.AddExtensionGrantValidator<PhoneCodeGrantValidator>();
6 });
7 }
简单验证
至此,扩展方式的核心工作已经准备完成,可以通过 postman 进行简单的实验。

非扩展授权方式
此方式也比较简单,校验手机号以及验证码的主体逻辑一致,只需要验证用户之后,通过 httpClient 去 IdentityServer 获取token,然后返回客户端即可。
其他:为了更好的安全,在登录失败后需要显式的标记登录失败,配合 Identity 的一些策略,可以对一段时间内登录失败次数过多的账户,进行锁定,防止用户信息泄露。

结尾
近期已经从公司离职了。近期也思考了很多,大城市与二线城市在做事风格上确实差别比较大;也有很多令人唏嘘的事情,改天总结一下。
扩展.Net Core Identity Server 授权方式,实现 手机号+ 验证码 登录的更多相关文章
- ASP.NET Core Identity 实战(2)——注册、登录、Claim
上一篇文章(ASP.NET Core Identity Hands On(1)--Identity 初次体验)中,我们初识了Identity,并且详细分析了AspNetUsers用户存储表,这篇我们将 ...
- .net core使用Ocelot+Identity Server统一网关验证
源码下载地址:下载 项目结构如下图: 在Identity Server授权中,实现IResourceOwnerPasswordValidator接口: public class IdentityVal ...
- Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式
一.前言 上一篇我分享了一篇关于 Asp.Net Core 中IdentityServer4 授权中心之应用实战 的文章,其中有不少博友给我提了问题,其中有一个博友问我的一个场景,我给他解答的还不够完 ...
- Identity Server 4 原理和实战(完结)_建立Identity Server 4项目,Client Credentials 授权实例
创建项目 dotnet new -i IdentityServer4.Templates 多出来的这些模板 adminUI用来测试,想要用再生产环境,需要交钱 结合core的 Identity来使用 ...
- 微服务系列之授权认证(二) identity server 4
1.简介 IdentityServer4 是为ASP.NET Core系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证授权框架. 官方文档:https://ident ...
- 【.NET Core项目实战-统一认证平台】第十四章 授权篇-自定义授权方式
[.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我介绍了如何强制令牌过期的实现,相信大家对IdentityServer4的验证流程有了更深的了解,本篇我将介绍如何使用自定义的授权方 ...
- 第15章 使用EntityFramework Core进行配置和操作数据 - Identity Server 4 中文文档(v1.0.0)
IdentityServer旨在实现可扩展性,其中一个可扩展点是用于IdentityServer所需数据的存储机制.本快速入门展示了如何配置IdentityServer以使用EntityFramewo ...
- Asp.net core Identity + identity server + angular 学习笔记 (第一篇)
用了很长一段时间了, 但是一直没有做过任何笔记,感觉 identity 太多东西要写了, 提不起劲. 但是时间一久很多东西都记不清了. 还是写一轮吧. 加深记忆. 这是 0-1 的笔记, 会写好多篇. ...
- ASP.NET Core Identity 实战(4)授权过程
这篇文章我们将一起来学习 Asp.Net Core 中的(注:这样描述不准确,稍后你会明白)授权过程 前情提要 在之前的文章里,我们有提到认证和授权是两个分开的过程,而且认证过程不属于Identity ...
随机推荐
- Spring框架的事务管理有哪些优点?
它为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式. 它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如 它支持声明式事务管理 ...
- Java 中的编译期常量是什么?使用它又什么风险?
公共静态不可变(public static final )变量也就是我们所说的编译期常量,这里 的 public 可选的.实际上这些变量在编译时会被替换掉,因为编译器知道这些 变量的值,并且知道这些变 ...
- 学习heartbeat-03t实现web服务的高可用案例及维护要点
8.Heartbeat实现web服务的高可用案例 8.1部署准备 通过web服务高可用案例来熟悉heatbeat软件的使用,用上面的两台服务器机器名分别为heartbeat-1-130和heartbe ...
- 设置IE的自动导包器
一丶打开IE设置: 快捷键:Ctrl+Alt+S 二丶将Add unambiguous imports on the fly 选中即可: 三丶设置好后别忘了"OK":
- Arduino 烧写bootloader
什么是bootloader 一般情况下微处理器写入程序时都通过专门的编程器进行烧写,但是也可以通过在MCU中预先写入一些程序来实现某些基本功能,这些预先写入的程序代码就是bootloader.这样每次 ...
- carsim的一些注意事项
1.carsim导入simulink中运行的目录下最好有simfile.sim文件,可以再carsim_2016.1_date中找到(文件夹内最下端) 2.carsim导入simulink中如果显示下 ...
- 微信小程序登录鉴权流程图
- java中 什么叫隐藏(Hide)? 最好给个例子
4.隐藏 技术核心和实例前面已经给出,这里只是给出大家对这种现象的一个定义而已,马克-to-win:子类重新定义一个与父类那里继承来的域变量完全相同的变量,称为域的隐藏.这里所谓隐藏是指子类拥有了 ...
- EMS邮件统计
前提条件:管理员拥有"Organization Management"权限.并且启用邮件跟踪日志. 1.统计时间段内邮件发送情况 案例任务:统计一段时间内服务器"MAIL ...
- String相关API-getBean()方法的使用
一.使用id //userService是Bean标签中配置的id属性值 UserService service = (UserService) context.getBean("userS ...