首页 > 其他分享 >.NET8 Identity Register

.NET8 Identity Register

时间:2024-05-22 16:31:10浏览次数:29  
标签:验证 Register 用户 userManager user NET8 var email Identity

分享给需要帮助的人:记一次 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。
这种方式的缺点很明显:

  1. 数据库冗余:未验证的用户仍然会被创建并保存在数据库中,可能会增加垃圾数据。
  2. 风险较高:未验证用户在短时间内可能会尝试恶意行为,需要额外的监控和限制措施。

优点如下:

  1. 实现简单:直接在用户创建时标记用户为未验证,逻辑简单易于实现。
  2. 用户体验:用户可以立即注册并部分使用系统功能,验证邮箱可以稍后进行。
  3. 安全可控:通过限制未验证用户的操作,可以在确保安全性的同时提供基本的用户体验。

更安全的方式是延迟用户创建,代码如下:

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.");
});

会发现它与第一个例子是相反的,它是用户注册后把数据保存在了临时的内存中,再向邮箱发送验证码。通过配置邮箱的时候,用验证码得到用户数据,并以此创建新的账号。

此做法的缺点也很明显:

  1. 实现复杂:需要额外的逻辑来保存临时注册信息并处理验证令牌。
  2. 用户体验:用户在注册后需要先验证邮箱才能完成注册流程,可能会导致部分用户流失。

优点如下:

  1. 避免垃圾用户:只有当用户验证了邮箱后,才会正式创建用户账户,减少垃圾用户数量。
  2. 安全性高:在用户点击确认链接前,账户信息不会进入数据库,降低被滥用的风险。
  3. 资源节省:避免创建大量未验证的用户,节省数据库存储和处理资源。

它们的适用场景如下:

  1. 延迟用户创建:适用于希望最大限度减少垃圾用户并确保用户邮箱有效性的场景,如高安全性要求的系统。
  2. 设置用户账户为未验证状态:适用于希望提供更流畅的用户体验,允许用户在验证邮箱前进行部分操作的场景,如社交平台或内容网站。

标签:验证,Register,用户,userManager,user,NET8,var,email,Identity
From: https://www.cnblogs.com/YataoFeng/p/18206455

相关文章

  • identityserver4使用
    参考网站https://blog.csdn.net/weixin_43847283/article/details/125708613请求路径请求参数client_id:simple_clientclient_secret:simple_client_secretgrant_type:client_credentials源码https://pan.baidu.com/s/1aWXzzl2fJ9ZeqAdXQfRCBA提取码:r6ef......
  • .net8 winform程序使用EntityFrameworkCore连接数据库
    在.NET8WinForms应用程序中使用EntityFramework(EF)Core,你需要按照以下步骤操作:1.添加EntityFrameworkCoreNuGet包。2.定义你的数据模型。3.创建数据库上下文(DbContext)。4.在数据库上下文中配置EntityFramework。5.使用EntityFrameworkCore的API来执行数据库操作。......
  • Cisco Identity Services Engine (ISE) 3.3 Patch 2 - 基于身份的网络访问控制和策略
    CiscoIdentityServicesEngine(ISE)3.3Patch2-基于身份的网络访问控制和策略实施系统思科身份服务引擎(ISE)-下一代NAC解决方案请访问原文链接:CiscoIdentityServicesEngine(ISE)3.3Patch2-基于身份的网络访问控制和策略实施系统,查看最新版。原创作品,转载......
  • Net8 webAPI 创建(傻瓜式入门)
    前沿学不止境(还是用windows学习吧mac太不友好了)让我来新建个Net8webapicore 然后看到这个页面选择不适用顶级语句然后配置你的目录  然后打开vsstudio看到编辑页面 可以在controllers里面创建新的.cs请求比如我新建了firstController  写入ge......
  • failed to register layer: ApplyLayer exit status 1 stdout: stderr: archive/tar:
    系统:麒麟V10处理器:飞腾2000,aarch64原因:安装算能,TPU-mlir环境产生 官方教程地址:2.开发环境配置—TPU-MLIR0.7文档(sophgo.com)从DockerHub https://hub.docker.com/r/sophgo/tpuc_dev 下载所需的镜像:dockerpullsophgo/tpuc_dev:latest 产生错误 sia......
  • ansible register
    register说明在Ansible中,register是一个关键字,用于捕获任务执行结果以及任务执行过程中产生的输出和值。使用register关键字可以将任务的执行结果存储到一个变量中,您可以在playbook后续的任务中使用这个变量。register 返回值类型使用register关键字时,Ansible会将任务......
  • Net8微服务实战
    前言学习杨中科老师开源项目在线英语网站微服务1.需求服务拆分2.项目源码项目类说明Peng.ASPNETCoreDistributedCacheHelper分布式缓存帮助类MemoryCacheHelper内存缓存帮助类UnitOfWorkFilter工作单元筛选器Peng.CommonsValidators文件夹Fluen......
  • Net8微服务之Consul、Ocelot、IdentityServer4
    前言情绪的尽头是沉默1.微服务概念1.1微服务发展分布式解决性能问题,微服务解决维护性、扩展性、灵活性。1.2微服务概念微服务(或称微服务架构),是一种现代化的软件架构方法,它将一个应用程序分解为多个小型、独立的服务单元,每个服务都负责特定的业务功能,并且可以独立开发、测......
  • blog.admin net8发布二级目录,及图片上传路径处理
    1、发布二级目录,修改以下配置,及对应的二级目录名跟配置的一致 2、图片上传a、修改后台api代码imgController.cspublicasyncTask<MessageModel<string>>InsertPicture([FromForm]UploadFileDtodto){if(dto.file==null||!dto.file.Any())returnFail......
  • net8 linux docker sqlserver报错error: 31 - Encryption(ssl/tls) handshake failed)
    asp.netcore升级到8.0后遇到数据库不能连接问题?_已解决_博问_博客园(cnblogs.com) 中文提示:连接数据库过程中发生错误,检查服务器是否正常连接字符串是否正确,错误信息:Aconnectionwassuccessfullyestablishedwiththeserver,butthenanerroroccurredduringthep......