背景

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

思路

  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

实现 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 授权方式,实现 手机号+ 验证码 登录的更多相关文章

  1. ASP.NET Core Identity 实战(2)——注册、登录、Claim

    上一篇文章(ASP.NET Core Identity Hands On(1)--Identity 初次体验)中,我们初识了Identity,并且详细分析了AspNetUsers用户存储表,这篇我们将 ...

  2. .net core使用Ocelot+Identity Server统一网关验证

    源码下载地址:下载 项目结构如下图: 在Identity Server授权中,实现IResourceOwnerPasswordValidator接口: public class IdentityVal ...

  3. Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式

    一.前言 上一篇我分享了一篇关于 Asp.Net Core 中IdentityServer4 授权中心之应用实战 的文章,其中有不少博友给我提了问题,其中有一个博友问我的一个场景,我给他解答的还不够完 ...

  4. Identity Server 4 原理和实战(完结)_建立Identity Server 4项目,Client Credentials 授权实例

    创建项目 dotnet new -i IdentityServer4.Templates 多出来的这些模板 adminUI用来测试,想要用再生产环境,需要交钱 结合core的 Identity来使用 ...

  5. 微服务系列之授权认证(二) identity server 4

    1.简介 IdentityServer4 是为ASP.NET Core系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证授权框架. 官方文档:https://ident ...

  6. 【.NET Core项目实战-统一认证平台】第十四章 授权篇-自定义授权方式

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我介绍了如何强制令牌过期的实现,相信大家对IdentityServer4的验证流程有了更深的了解,本篇我将介绍如何使用自定义的授权方 ...

  7. 第15章 使用EntityFramework Core进行配置和操作数据 - Identity Server 4 中文文档(v1.0.0)

    IdentityServer旨在实现可扩展性,其中一个可扩展点是用于IdentityServer所需数据的存储机制.本快速入门展示了如何配置IdentityServer以使用EntityFramewo ...

  8. Asp.net core Identity + identity server + angular 学习笔记 (第一篇)

    用了很长一段时间了, 但是一直没有做过任何笔记,感觉 identity 太多东西要写了, 提不起劲. 但是时间一久很多东西都记不清了. 还是写一轮吧. 加深记忆. 这是 0-1 的笔记, 会写好多篇. ...

  9. ASP.NET Core Identity 实战(4)授权过程

    这篇文章我们将一起来学习 Asp.Net Core 中的(注:这样描述不准确,稍后你会明白)授权过程 前情提要 在之前的文章里,我们有提到认证和授权是两个分开的过程,而且认证过程不属于Identity ...

随机推荐

  1. 学习k8s(三)

    一.Kubernetes核心概念 1.Kubernetes介绍 Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署,调度和节点集群间扩展.如果你曾经用过Docker容器技术部署 ...

  2. IOS动态调试汇总-傻瓜版教程

    参考博客: https://juejin.cn/post/6872764160640450574#heading-4 (断点后续指令) https://www.jianshu.com/p/67f08a ...

  3. (stm32f103学习总结)—初识stm32

    STM32分类 STM32的命名方法 怎样选择合适的MCU 一个原则:花最少的钱,做最多的事 在确定项目需求的情况下,一般按照下面的顺序来选择合适的MCU 如何分配原理图引脚 如何寺找引脚的功能说明 ...

  4. 3.3转1.8V(电平转换)

  5. Flutter入门教程(四)第一个flutter项目解析

    一.创建一个Flutter工程 1.1 命令行创建 首先我们找一个空目录用来专门存放flutter项目,然后在路径中直接输入cmd: 使用 flutter create <projectname ...

  6. layui表单使用开关滑块和复选框,渲染后台数据方法

    提示:整个表格要在form标签内 定义开关模板 <div class="layui-form" lay-filter="layuiadmin-app-form-li ...

  7. JQuery基础修炼-样式篇

    jQuery对象转化成DOM对象 jQuery库本质上还是JavaScript代码,它只是对JavaScript语言进行包装处理,为了是提供更好更方便快捷的DOM处理与开发常见中经常使用的功能.我们可 ...

  8. CSS3新特性的概述

    CSS3的新特性大致分为以下六类 1.CSS3选择器 2.CSS3边框与圆角 3.CSS3背景与渐变 4.CSS3过渡 5.CSS3变换 6.CSS3动画 下面分别说一说以上六类都有哪些内容 CSS3 ...

  9. JDBC/Mybatis连接数据库报错:The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.

    造成这个的原因是maven导入MyBatis的时候会自动导入最新版本的8.0.11,然后8.0.11采用了新驱动,之前版本会报错. 当我们使用高版本的MySQL驱动时可以在获取数据库的连接getCon ...

  10. PAT B1013 数素数

    输入样例: 5 27   输出样例: 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 解题思路: 从2开始 ...