扩展.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 ...
随机推荐
- Pycharm连接MySQL步骤及注意点
1.数据库连接修改MySQL: 默认:MySQLDB #MySQLDB只支持Python2,暂不支持python3,所以要修改, 修改成:pymysql,在每个项目中都需要先导入pymysql模块, ...
- java-file类 hei
File类 /* java.io.File类 文件和目录轮径的抽象形式 java把电脑种的文件和文件夹,封装为一个file类,我们可以使用file类对文件和文件夹进行曹祖 使用File类的方法 创建一 ...
- Netty学习摘记 —— 初识编解码器
本文参考 本篇文章是对<Netty In Action>一书第十章"编解码器框架"的学习摘记,主要内容为解码器和编码器 编解码器实际上是一种特殊的ChannelHand ...
- es8(字符串,对象)
es8(字符串,对象) 字符串补白: let str = "abc"; let a = str.padEnd(5); let b = str.padStart(5); let c ...
- error: failed to push some refs to 'git@gitee.com:xxxx'
出现错误的主要原因是向上仓库的一些文件(README.md,LICENSE等文件)不在本地代码目录中 git pull --rebase origin master 通过这行命令, 可以解决 注释福利 ...
- github账号&文章选题
----------------------------------------------------------- https://github.com/yanpanjiao github ...
- c++对c的拓展_内联函数
目的:保持处理宏的高效及安全性 解决的问题:1.c中预处理宏有些难以发现的问题 2.c++ 中预处理不能访问类成员,不能作用类的成员函数 作用:无函数调用时开销,又可像普通函数般进行参数.返回值类型安 ...
- LC-24
[24. 两两交换链表中的节点](https://leetcode-cn.com/problems/swap-nodes-in-pairs/) 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的 ...
- Hash冲突以及解决
哈希函数:它把一个大范围的数字哈希(转化)成一个小范围的数字,这个小范围的数对应着数组的下标.使用哈希函数向数组插入数据后,这个数组就是哈希表. 冲突 当冲突产生时,一个方法是通过系统的方法找到数组的 ...
- Java数组的常见算法2
1. 求数值型数组中元素的最大值.最小值.平均值.总值等 2. 数组的复制.反转.查找(线性查找.二分法查找)