.NET8 Identity Register
分享给需要帮助的人:记一次 IdentityAPI 中注册的源码解读:设置用户账户为未验证状态,以及除此之外更安全的做法: 延迟用户创建。包含了对优缺点的说明,以及适用场景。
在ASP.NET 8 Identity 中注册API的源码如下:
routeGroup.MapPost("/register", async Task<Results<Ok, ValidationProblem>>
([FromBody] RegisterRequest registration, HttpContext context, [FromServices] IServiceProvider sp) =>
{
var userManager = sp.GetRequiredService<UserManager<TUser>>();
if (!userManager.SupportsUserEmail)
{
throw new NotSupportedException($"{nameof(MapIdentityApi)} requires a user store with email support.");
}
var userStore = sp.GetRequiredService<IUserStore<TUser>>();
var emailStore = (IUserEmailStore<TUser>)userStore;
var email = registration.Email;
if (string.IsNullOrEmpty(email) || !_emailAddressAttribute.IsValid(email))
{
return CreateValidationProblem(IdentityResult.Failed(userManager.ErrorDescriber.InvalidEmail(email)));
}
var user = new TUser { EmailConfirmed = false }; // 标记为未验证
await userStore.SetUserNameAsync(user, email, CancellationToken.None);
await emailStore.SetEmailAsync(user, email, CancellationToken.None);
var result = await userManager.CreateAsync(user, registration.Password);
if (!result.Succeeded)
{
return CreateValidationProblem(result);
}
await SendConfirmationEmailAsync(user, userManager, context, email);
return TypedResults.Ok();
});
routeGroup.MapGet("/confirm-email", async Task<IResult>
([FromQuery] string userId, [FromQuery] string token, [FromServices] UserManager<TUser> userManager) =>
{
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
return TypedResults.BadRequest("Invalid user.");
}
var result = await userManager.ConfirmEmailAsync(user, token);
if (!result.Succeeded)
{
return TypedResults.BadRequest("Email confirmation failed.");
}
user.EmailConfirmed = true; // 更新为已验证
await userManager.UpdateAsync(user);
return TypedResults.Ok("Email confirmed successfully.");
});
会发现它在注册的时候使用邮箱作为用户名,配置了邮箱和密码。但是它在发送邮箱验证码之前,就已经通过CreateAsync创建好了账号。这种方式叫做设置用户账户为未验证状态,将 EmailConfirmed 设置为 false,邮箱验证确认后设置为true。
这种方式的缺点很明显:
- 数据库冗余:未验证的用户仍然会被创建并保存在数据库中,可能会增加垃圾数据。
- 风险较高:未验证用户在短时间内可能会尝试恶意行为,需要额外的监控和限制措施。
优点如下:
- 实现简单:直接在用户创建时标记用户为未验证,逻辑简单易于实现。
- 用户体验:用户可以立即注册并部分使用系统功能,验证邮箱可以稍后进行。
- 安全可控:通过限制未验证用户的操作,可以在确保安全性的同时提供基本的用户体验。
更安全的方式是延迟用户创建,代码如下:
routeGroup.MapPost("/register", async Task<IResult>
([FromBody] RegisterRequest registration, HttpContext context, [FromServices] IServiceProvider sp) =>
{
var userManager = sp.GetRequiredService<UserManager<TUser>>();
if (!userManager.SupportsUserEmail)
{
throw new NotSupportedException($"{nameof(MapIdentityApi)} requires a user store with email support.");
}
var userStore = sp.GetRequiredService<IUserStore<TUser>>();
var emailStore = (IUserEmailStore<TUser>)userStore;
var email = registration.Email;
if (string.IsNullOrEmpty(email) || !_emailAddressAttribute.IsValid(email))
{
return CreateValidationProblem(IdentityResult.Failed(userManager.ErrorDescriber.InvalidEmail(email)));
}
// 生成验证令牌并发送确认邮件
var verificationToken = GenerateVerificationToken();
await SendVerificationEmailAsync(email, verificationToken, context);
// 临时保存注册信息
SaveTemporaryRegistrationInfo(registration, verificationToken);
return TypedResults.Ok("Please confirm your email.");
});
routeGroup.MapGet("/confirm-email", async Task<IResult>
([FromQuery] string token, [FromServices] IServiceProvider sp) =>
{
var registration = GetTemporaryRegistrationInfoByToken(token);
if (registration == null)
{
return TypedResults.BadRequest("Invalid or expired token.");
}
var userManager = sp.GetRequiredService<UserManager<TUser>>();
var user = new TUser();
await userStore.SetUserNameAsync(user, registration.Email, CancellationToken.None);
await emailStore.SetEmailAsync(user, registration.Email, CancellationToken.None);
var result = await userManager.CreateAsync(user, registration.Password);
if (!result.Succeeded)
{
return CreateValidationProblem(result);
}
return TypedResults.Ok("Email confirmed and user created.");
});
会发现它与第一个例子是相反的,它是用户注册后把数据保存在了临时的内存中,再向邮箱发送验证码。通过配置邮箱的时候,用验证码得到用户数据,并以此创建新的账号。
此做法的缺点也很明显:
- 实现复杂:需要额外的逻辑来保存临时注册信息并处理验证令牌。
- 用户体验:用户在注册后需要先验证邮箱才能完成注册流程,可能会导致部分用户流失。
优点如下:
- 避免垃圾用户:只有当用户验证了邮箱后,才会正式创建用户账户,减少垃圾用户数量。
- 安全性高:在用户点击确认链接前,账户信息不会进入数据库,降低被滥用的风险。
- 资源节省:避免创建大量未验证的用户,节省数据库存储和处理资源。
它们的适用场景如下:
- 延迟用户创建:适用于希望最大限度减少垃圾用户并确保用户邮箱有效性的场景,如高安全性要求的系统。
- 设置用户账户为未验证状态:适用于希望提供更流畅的用户体验,允许用户在验证邮箱前进行部分操作的场景,如社交平台或内容网站。
.NET8 Identity Register的更多相关文章
- iOS-开发者相关的几种证书
目录 引言 写在前面 一App IDbundle identifier 二设备Device 三开发证书Certificates 证书的概念 数字证书的概念 iOS开发证书 iOS开发证书的根证书 申请 ...
- [转载]iOS Provisioning Profile(Certificate)与Code Signing详解
原文:http://blog.csdn.net/phunxm/article/details/42685597 引言 关于开发证书配置(Certificates & Identifiers & ...
- iOS Provisioning Profile(Certificate)与Code Signing详解
引言 关于开发证书配置(Certificates & Identifiers & Provisioning Profiles),相信做 iOS 开发的同学没少被折腾.对于一个 iOS ...
- 【上传AppStore】iOS项目上传到AppStore步骤流程(第三章) - 基本信息总汇
一.App ID(bundle identifier) App ID即Product ID,用于标识一个或者一组App. App ID应该和Xcode中的Bundle Identifier是一致(Ex ...
- 【转】iOS Provisioning Profile(Certificate)与Code Signing详解 -- 待看
原文网址:http://blog.sina.com.cn/s/blog_82c8198f0102vy4j.html 引言 关于开发证书配置(Certificates & Identifiers ...
- 【转】 iOS Provisioning Profile(Certificate)与Code Signing详解
原文:http://blog.csdn.net/phunxm/article/details/42685597 引言 关于开发证书配置(Certificates & Identifiers & ...
- iOS 证书那些事
关于开发证书配置(Certificates & Identifiers & Provisioning Profiles),相信做iOS开发的同学没少被折腾.对于一个iOS开发小白.半吊 ...
- 网络协议 终章 - GTP 协议:复杂的移动网络
前面都是讲电脑上网的情景,今天我们就来认识下使用最多的移动网络上网场景. 移动网络的发展历程 你一定知道手机上网有 2G.3G.4G 的说法,究竟这都是什么意思呢?有一个通俗的说法就是 ...
- Hyperledger Fabric CA User’s Guide——开始(三)
Fabric CA User’s Guide——开始 先决条件 安装Go 1.9+ 设置正确的GOPATH环境变量 安装了libtool和libtdhl-dev包 下面是在Ubuntu上安装libto ...
- iOS 证书详解
引言 关于开发证书配置(Certificates & Identifiers & Provisioning Profiles),相信做iOS开发的同学没少被折腾.对于一个iOS开发小白 ...
随机推荐
- Java 抽象类与方法:实现安全性与代码重用
Java 内部类 简介 在 Java 中,可以嵌套类(即类内部的类),称为内部类.嵌套类的目的是将属于一起的类分组,从而使您的代码更可读和可维护. 访问内部类 要访问内部类,请创建外部类的对象,然后创 ...
- Numpy通用函数及向量化计算
Python(Cpython)对于较大数组的循环操作会比较慢,因为Python的动态性和解释性,在做每次循环时,必须做数据类型的检查和函数的调度. Numpy为很多类型的操作提供了非常方便的.静态类型 ...
- C语言 02 安装
C 语言的编译器有很多,其中最常用的是 GCC,这里以安装 GCC 为例. Windows 这里以 Windows 11 为例 官方下载地址:https://www.mingw-w64.org/ 选择 ...
- C#开发的绑定类型默认应用例子 - 开源研究系列文章
这次在用C#编写一个看图软件小工具,然后其它的基本完成了,就是绑定看图软件到那些个图片扩展名的时候碰到了问题,就是如何将看图软件绑定图片文件的默认应用,以及解绑默认应用.这个涉及到注册表操作,但是找度 ...
- 基于istio实现多集群流量治理
本文分享自华为云社区<基于istio实现多集群流量治理>,作者: 可以交个朋友. 一 背景 对多云.混合云等异构基础设施的服务治理是Istio重点支持的场景之一.为了提高服务的可用性,避免 ...
- 双端队列的基本实现【数据结构与算法—TypeScript 实现】
笔记整理自 coderwhy 『TypeScript 高阶数据结构与算法』课程 特性 本质:允许队列在两端进行 入队 和 出队 操作 设计 实现方式:基于 数组 实现 属性: data:存放队列元素 ...
- 【直播合集】HDC.Together 2023 精彩回顾!收藏勿错过~
HDC.Together 2023 主题演讲 万象复兴,热潮澎湃,HarmonyOS 全面进化,迈入新纪元.以创新改变世界,以生态驱动未来.扬帆起航,就在此刻.新版本.新体验.新流量.新商业.新机遇. ...
- 【Python3.10.4】Centos7 ,centos8,centos9源码安装 python3.10.4 解释器
1.检查是否安装wget如果没有安装则: 终端执行: yum -y install wget 2.下载python3源码包 终端执行: wget https://www.python.org/ftp/ ...
- 重新整理 .net core 实践篇———承载[外篇]
前言 简单介绍一下承载. 正文 名称叫做承载,其实就是.net core 定义的一套长期运行的服务的规范. 这个服务可以是web服务,也可以是其他服务,比如tcp,或者一些监控服务. 这里以监控服务为 ...
- 重新整理数据结构与算法(c#)——KMP破解[二十七]
前言 很多人把KMP和暴力破解分开,其实KMP就是暴力破解,整个高大上的名字,难道还不是去试错匹配吗? KMP是这样子的,比如说: 绿色部分是我要匹配的. 按照一般写法是这样子的: ABABA 去匹配 ...