最新.NET8 身份:注册、登录、电子邮件确认和双因素身份验证 (2FA)

1cb6f2bb40c29b304afd72607268a2fe.jpeg

本文源代码获取:文末

启动一个新项目并从头开始处理身份验证和授权可能会筋疲力尽。它通常涉及创建登录和注册功能,这可能很耗时。管理刷新令牌和实现双因素身份验证等挑战增加了复杂性。

值得庆幸的是,随着 .NET 8 的到来,这些任务大大简化,只需要最少的配置。

在本文中,我将指导你完成各种标识配置方案,包括:

  • 基本注册和登录。

  • 使用持有者令牌保护终结点。

  • 检索用户信息。

  • 电子邮件确认。

  • 重新发送电子邮件确认。

  • 更改默认设置并添加自定义用户属性。

  • 配置双因素身份验证 (MVC)。

先决条件:.NET 8 或更高版本。

在继续之前,让我概述一下我当前的环境设置:

  • .NET Web API 应用程序。

  • Visual Studio 2022 年。

  • 利用SQLite模拟真实数据库,而不是内存数据库。

接下来,让我们安装必要的 NuGet 包。

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore  
dotnet add package Microsoft.EntityFrameworkCore.Design  
dotnet add package Microsoft.EntityFrameworkCore.Sqlite  
dotnet add package Microsoft.EntityFrameworkCore.Tools

基本注册和登录。

首先,首先创建所有身份生成的表的存储位置。

// IdentityUser is the Microsoft base identity class.  
// creates empty scheme with just all the identity tables.  
class AppDbContext : IdentityDbContext<IdentityUser>  
{  
    public AppDbContext(DbContextOptions options) : base(options)  
    {  
    }  
}

接下来,我们将注册身份验证和授权中间件,从而灵活地在持有者令牌或 Cookie 之间进行选择。在此示例中,我们将选择持有者令牌,以便于在登录时检索持有者令牌。

// Program.cs  
builder.Services.AddAuthentication()  
                .AddBearerToken(IdentityConstants.BearerScheme);  
  
builder.Services.AddAuthorizationBuilder();

之后,我们将注册我们的 ,在此示例中,它是一个 SQLite 数据库。

// Program.cs  
builder.Services.AddDbContext<AppDbContext>(options =>  
{  
    options.UseSqlite("DataSource=app.db");  
});

之后,我们需要将 作为身份存储连接,以便所有操作都指向 SQLite 数据库。

// Program.cs  
builder.Services.AddIdentityCore\<IdentityUser>()  
    .AddEntityFrameworkStores\<AppDbContext>()  
    .AddApiEndpoints();

最后,我们需要将所有端点添加为应用程序端点的一部分。这可以通过在构建生成器后映射标识终结点来实现。

app.MapIdentityApi<IdentityUser>();

您的Program.cs文件现在应类似于此结构。

using Microsoft.AspNetCore.Identity;  
using Microsoft.EntityFrameworkCore;  
  
var builder = WebApplication.CreateBuilder(args);  
  
builder.Services.AddControllers();  
builder.Services.AddEndpointsApiExplorer();  
builder.Services.AddSwaggerGen();  
  
builder.Services.AddAuthentication()  
                .AddBearerToken(IdentityConstants.BearerScheme);  
  
builder.Services.AddAuthorizationBuilder();  
  
builder.Services.AddDbContext<AppDbContext>(options =>  
{  
    options.UseSqlite("DataSource=app.db");  
});  
  
builder.Services.AddIdentityCore<IdentityUser>()  
    .AddEntityFrameworkStores<AppDbContext>()  
    .AddApiEndpoints();  
  
var app = builder.Build();  
  
app.MapIdentityApi<IdentityUser>();  
  
  
app.UseSwagger();  
app.UseSwaggerUI();  
app.UseHttpsRedirection();  
app.UseAuthorization();  
app.MapControllers();  
  
app.Run();

在运行应用程序之前,请不要忘记添加新的迁移,并通过从包管理器控制台执行以下命令来更新数据库。

Add-Migration initial  
Update-Database

现在,运行应用程序并打开 Swagger。您应该会看到自动生成的终结点集合。

f1e73adb30ca89ddfde8f2ab2b1716b5.png

生成的身份终结点。

尝试注册新用户。如果尝试使用无效的电子邮件地址或不符合条件的密码创建新用户,则标识将返回错误以及发生的具体详细信息。

76937e4a2d2e56730ebe712c25c871b8.png

e65c87cc2ffaacd72415e570cdf313d7.png

现在,让我们继续登录过程。如果提供的电子邮件或密码无效,则身份将返回 401 未授权状态。

afedeb27c107796b6e966331a2758e02.png

c0edf95630749cb6e9d054cb33c21069.png

使用持有者令牌保护终结点。

将身份验证配置为返回持有者令牌后,我们需要将此持有者令牌用于任何需要授权的受保护终结点。

必须注意的是,生成的令牌不是标准的 JSON Web 令牌 (JWT)。此决定是有意为之的,因为内置标识主要用于简单方案。令牌选项并不意味着充当功能齐全的身份服务提供商或令牌服务器,而是无法使用 cookie 的客户端的 cookie 选项的替代方案。

现在,让我们尝试访问授权端点,特别是 /manage/info 端点。如果您尝试从 Swagger 访问它,它将显示未经授权的错误。接下来,使用 Postman 登录,保存 accessToken,并在调用 /manage/info 端点时包含它。这一次,它应该返回所需的结果。

5069a02f00add5bfaa7d52fb0a123850.png

38bf56d30442f0b16144cafbc6d8490e.png

检索用户信息

要随时检索当前登录用户的信息,请使用存储当前登录用户信息的类。ClaimsPrincipal

要使用最小 API 方法实现此目的,请执行以下操作:

app.Map("/", (ClaimsPrincipal user) => $"Hello {user.Identity!.Name}")  
    .RequireAuthorization();

对于控制器方法:

[ApiController]  
[Route("[controller]")]  
public class TestController: ControllerBase  
{  
    [HttpGet]  
    [Authorize]  
    public string Get()  
    {  
        return User.Identity!.Name;  
    }  
}

请务必注意,上述示例使用“**!”**运算符,这意味着标识应始终存在,而不可能为 null。此假设成立,因为两者都是需要事先登录的授权端点。但是,建议验证标识是否存在,以防止潜在的 null 引用异常。

221f9636536621d9ddbd658c9a8ea2a7.png

832769938657b5f9709038b60f3c39de.png

电子邮件确认

Microsoft建议使用SendGrid或其他电子邮件服务发送电子邮件,而不是SMTP。SMTP 很难保护和正确设置。

在本教程中,SendGrid 用于发送电子邮件。发送电子邮件需要 SendGrid 帐户和密钥。请参阅免费开始使用 SendGrid,注册免费的 SendGrid 帐户。

1- 在文件中配置 SendGrid 所需信息。避免将这些设置直接添加到appsettings.json文件中,以降低安全风险。secrets.json

{  
  "SendGridKey": "your send-grid key",  
  "From": "your registered email",  
  "Name": "your registered name"  
}

805c708acce8faca41c7b026ae7b6da7.png

注册电子邮件和姓名

86b5961511653bf9011c3b6289e9ba2e.png

SendGrid API 密钥

2- 安装必要的 NuGet 包。

dotnet add package SendGrid  
dotnet add package SendGrid.Extensions.DependencyInjection

3-实现IEmailSender

要实现,请使用类似于以下内容的代码进行创建:IEmailSenderEmailSender.cs

using Microsoft.AspNetCore.Identity.UI.Services;  
using SendGrid;  
using SendGrid.Helpers.Mail;  
  
namespace Identity;  
  
public class EmailSender : IEmailSender  
{  
    private readonly ILogger _logger;  
    private readonly IConfiguration _configuration;  
  
    public EmailSender(IConfiguration configuration, ILogger<EmailSender> logger)  
    {  
        _configuration = configuration;  
        _logger = logger;  
    }  
  
    public async Task SendEmailAsync(string toEmail, string subject, string message)  
    {  
        var sendGridKey = _configuration["SendGridKey"];  
        ArgumentNullException.ThrowIfNullOrEmpty(sendGridKey, nameof(sendGridKey));  
        await Execute(sendGridKey, subject, message, toEmail);  
    }  
  
    public async Task Execute(string apiKey, string subject, string message, string toEmail)  
    {  
        var client = new SendGridClient(apiKey);  
  
        var msg = new SendGridMessage()  
        {  
            From = new EmailAddress(_configuration["From"], _configuration["Name"]),  
            Subject = subject,  
            PlainTextContent = message,  
            HtmlContent = message  
        };  
  
        msg.AddTo(new EmailAddress(toEmail));  
  
        // Disable click tracking.  
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html  
        msg.SetClickTracking(false, false);  
  
        var response = await client.SendEmailAsync(msg);  
        _logger.LogInformation(response.IsSuccessStatusCode  
                               ? $"Email to {toEmail} queued successfully!"  
                               : $"Failure Email to {toEmail}");  
    }  
}

4- 将 EmailService 注入其中以启用其使用。Program.cs

// Program.cs  
builder.Services.AddTransient<IEmailSender, EmailSender>();

5- 强制登录确认并集成 SendGrid 服务。

现在,如果您尝试再次注册,它将起作用,但您会注意到没有发送电子邮件确认。此外,用户无需任何确认即可登录。

为了解决这个问题并确保正确的流程,我们需要首先在签名过程中要求电子邮件确认。然后,我们需要集成 SendGrid 服务,以便标识框架可以利用它。

// Program.cs  
  
builder.Services.AddIdentityCore<IdentityUser>(options =>  
{  
    options.SignIn.RequireConfirmedEmail = true;  
})  
  
builder.Services.AddSendGrid(options =>  
    options.ApiKey = builder.Configuration["SendGridKey"]!  
);

您的文件现在应类似于此结构。Program.cs

using Identity;  
using Microsoft.AspNetCore.Identity;  
using Microsoft.AspNetCore.Identity.UI.Services;  
using Microsoft.EntityFrameworkCore;  
using SendGrid.Extensions.DependencyInjection;  
using System.Security.Claims;  
  
var builder = WebApplication.CreateBuilder(args);  
  
builder.Services.AddControllers();  
builder.Services.AddEndpointsApiExplorer();  
builder.Services.AddSwaggerGen();  
  
builder.Services.AddAuthentication()  
                .AddBearerToken(IdentityConstants.BearerScheme);  
  
builder.Services.AddSendGrid(options =>  
    options.ApiKey = builder.Configuration["SendGridKey"]!  
);  
  
builder.Services.AddTransient<IEmailSender, EmailSender>();  
  
builder.Services.AddAuthorizationBuilder();  
  
builder.Services.AddDbContext<AppDbContext>(options =>  
{  
    options.UseSqlite("DataSource=app.db");  
});  
  
builder.Services.AddIdentityCore<IdentityUser>(options =>  
{  
    options.SignIn.RequireConfirmedEmail = true;  
})  
    .AddEntityFrameworkStores<AppDbContext>()  
    .AddApiEndpoints();  
  
var app = builder.Build();  
  
app.MapIdentityApi<IdentityUser>();  
  
app.Map("/", (ClaimsPrincipal user) => $"Hello {user.Identity!.Name} from minimal apis.")  
    .RequireAuthorization();  
  
app.UseSwagger();  
app.UseSwaggerUI();  
app.UseHttpsRedirection();  
app.UseAuthorization();  
app.MapControllers();  
  
app.Run();

现在,是时候测试我们的应用程序了。尝试注册一个新用户,并检查您的电子邮件以获取确认链接。单击该链接应将数据库中的确认状态更改为 TRUE。

请记住检查您的垃圾邮件文件夹,因为电子邮件通常最终会在那里。此外,请注意,电子邮件在发送之前会排队,因此预计会有几秒钟的轻微延迟。

0ba3eb2e0dce09631d3ef5e49ba42ba4.png

注册新用户

87e5f7e4bf077f7d53972cd490d0906e.png

Visual Studio 日志

cb541d801230bca95cc5b59da6efc4f3.png

在数据库中创建的用户

8f66ed0b5919122ee773ddd7da769508.png

确认链接电子邮件

通过单击链接“单击此处”,它应该将您重定向回 localhost,并将用户在数据库中的确认状态更新为 TRUE。

a174ce5ac73722c55d1f8e7116e1824d.png

确认消息

88763ea1d7047397ca654431fb184722.png

确认状态更改为 TRUE

最后,通过注入 SendGrid 服务,我们可以简化 EmailService,从而减少代码行数。

这是该版本的更新版本,可以有效处理所有测试方案。EmailService

using Microsoft.AspNetCore.Identity.UI.Services;  
using SendGrid;  
using SendGrid.Helpers.Mail;  
  
public class EmailSender : IEmailSender  
{  
    private readonly ILogger _logger;  
    private readonly IConfiguration _configuration;  
    private readonly ISendGridClient _sendGridClient;  
  
  
    public EmailSender(IConfiguration configuration, ILogger<EmailSender> logger, ISendGridClient sendGridClient)  
    {  
        _configuration = configuration;  
        _logger = logger;  
        _sendGridClient = sendGridClient;  
    }  
  
    public async Task SendEmailAsync(string toEmail, string subject, string message)  
    {  
        var msg = new SendGridMessage()  
        {  
            From = new EmailAddress(_configuration["From"], _configuration["Name"\]),  
            Subject = subject,  
            PlainTextContent = message,  
            HtmlContent = message  
        };  
        msg.AddTo(new EmailAddress(toEmail));  
  
        var response = await _sendGridClient.SendEmailAsync(msg);  
        _logger.LogInformation(response.IsSuccessStatusCode  
                               ? $"Email to {toEmail} queued successfully!"  
                               : $"Failure Email to {toEmail}");  
    }  
}

重新发送电子邮件确认

您当然需要重新发送确认电子邮件的选项,这对任何应用程序都至关重要。.NET 8 Identity 提供重新发送确认终结点:只需调用并设置正文,如下所示:.NET 8 Identity provides a resend confirmation endpoint: just call and set the body as follows:/resendConfirmationEmail

{
"Email": "user email"
}

683343d68044f44a48cb16c2f94deb93.png

dbb978c9d5ce5d05dfbb285c177a0b00.png

更改默认设置并添加自定义用户属性

Microsoft 标识还提供修改默认设置或引入新的自定义属性的功能。

调整默认设置。

builder.Services.Configure<IdentityOptions>(options =>  
{  
    // Password settings.  
    options.Password.RequireDigit = true;  
    options.Password.RequireLowercase = true;  
    options.Password.RequireNonAlphanumeric = true;  
    options.Password.RequireUppercase = true;  
    options.Password.RequiredLength = 6;  
    options.Password.RequiredUniqueChars = 1;  
  
    // Lockout settings.  
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);  
    options.Lockout.MaxFailedAccessAttempts = 5;  
    options.Lockout.AllowedForNewUsers = true;  
  
    // User settings.  
    options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.\_@+";  
    options.User.RequireUniqueEmail = false;  
});

将新的自定义属性添加到用户属性中。这可以通过创建一个新的用户类来实现,该类继承自 。

class AppUser : IdentityUser   
{  
    public int MyProperty { get; set; }  
}

随后,需要添加新的迁移,并且应将新属性集成到表中。

请记住将应用程序中出现的每个项更新为IdentityUserAppUser

配置双因素身份验证 (MVC)

在我们开始之前,这里有一些概念,在我们开始之前最好知道。

什么是多重身份验证 (MFA)?

多重身份验证 (MFA) 通过要求用户在登录过程中提供其他形式的身份标识来增强安全性。这可能包括从手机输入代码、使用 FIDO2 密钥或提供指纹扫描。通过要求第二种形式的身份验证,MFA 使攻击者更难获得未经授权的访问,因为附加因素不容易获得或复制。

什么是双因素身份验证 (2FA)?

双因素身份验证 (2FA) 类似于 MFA 的子集,但不同之处在于 MFA 可能需要两个或多个因素来证明身份。

什么是TOTP(基于时间的一次性密码算法)?

使用 ASP.NET Core Identity 时,默认支持使用 TOTP 的 MFA。此方法可与任何合规的身份验证器应用一起使用,包括:

  • Microsoft 身份验证器

  • 谷歌身份验证器

什么是 MFA 短信?

与密码身份验证(单因素)相比,带有 SMS 的 MFA 大大提高了安全性。但是,不再建议使用 SMS 作为第二个因素。对于此类实现,存在太多已知的攻击媒介。

什么是在线快速身份识别 (FIDO)?
FIDO 是无密码身份验证的开放标准,允许用户在没有密码的情况下登录。FIDO2 密钥(通常是 USB,但也包括蓝牙或 NFC)通过消除密码风险来增强安全性。
它们目前被认为是最安全的 MFA 方法。

现在,让我们开始配置 2FA。

我们将利用 2FA 与 MVC 项目的集成。首次创建新的 MVC 项目时,可以选择从一开始就生成标识系统,包括“注册”、“登录”和“2FA”。我们的第一步是创建一个新的 MVC 项目。

在 Visual Studio 2022 中,可以完成以下操作:

ec3cfbb96a74353c56b857c6b7538382.png

选择个人帐户标识选项。

d9e8eeb3f814a6364520be97c7cb5d44.png

或者,您可以使用命令行。

dotnet new mvc -n "2FA" -au individual

创建的项目应如下所示。

aaaa25a34f9f61354053703b198f5f61.png

现在,运行创建的项目。应显示一个带有登录和注册按钮的欢迎页面。

1a86c26f4ea74cf75ec5d57e19f6089d.png

单击“注册”以创建新用户。

d6acb498952ef120aa9a11383f083819.png

单击“单击此处确认您的帐户”以确认您的电子邮件。

0df48f760f2539cafd6087f536d3c23c.png

fb64175a0a56f4aefcdf2aef9115a622.png

接下来,注销,然后再次登录。点击右上角的电子邮件。

f6b8bbb2e598c52dcbc5d68f1fdeadc8.png

导航到“双因素身份验证”部分。

780c8908cdbdc20dc595f63274760f9e.png

单击“添加身份验证器应用程序” 应显示双因素身份验证设置屏幕。

f46e97c5c7630d679ae30a54d0f3b87a.png

如您所见,它是开箱即用的,但没有扫描二维码的选项;它仅适用于代码。

现在,让我们添加二维码功能,允许用户只需扫描二维码即可自动添加密钥。

我们需要访问为我们创建的生成的 cshtml 页面。默认情况下,这些页面位于目录中。但是,如果尚未展开目录,则 dotnet 不会显示文件,除非生成了文件。Areas/Identity/Pages

为此,请运行该命令。

dotnet tool install -g dotnet-aspnet-codegenerator

接下来,我们需要安装所有必需的 NuGet 包。

dotnet add pacakge Microsoft.EntityFrameworkCore.Design  
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

然后,运行代码生成器并指定要检索的页面。默认情况下,如果将其留空,它将生成许多页面。但是,为了避免不必要的页面使您的项目混乱,我们将指定我们需要的特定页面。

cd "your project directory"  
dotnet aspnet-codegenerator identity -dc _2FA.Data.ApplicationDbContext --files "Account.Manage.EnableAuthenticator"

若要查看可生成的所有文件,请运行该命令。

dotnet aspnet-codegenerator identity -dc _2FA.Data.ApplicationDbContext -lf

以下是可以生成的所有文件的完整列表。

File List:  
Account.\_StatusMessage  
Account.AccessDenied  
Account.ConfirmEmail  
Account.ConfirmEmailChange  
Account.ExternalLogin  
Account.ForgotPassword  
Account.ForgotPasswordConfirmation  
Account.Lockout  
Account.Login  
Account.LoginWith2fa  
Account.LoginWithRecoveryCode  
Account.Logout  
Account.Manage.\_Layout  
Account.Manage.\_ManageNav  
Account.Manage.\_StatusMessage  
Account.Manage.ChangePassword  
Account.Manage.DeletePersonalData  
Account.Manage.Disable2fa  
Account.Manage.DownloadPersonalData  
Account.Manage.Email  
Account.Manage.EnableAuthenticator  
Account.Manage.ExternalLogins  
Account.Manage.GenerateRecoveryCodes  
Account.Manage.Index  
Account.Manage.PersonalData  
Account.Manage.ResetAuthenticator  
Account.Manage.SetPassword  
Account.Manage.ShowRecoveryCodes  
Account.Manage.TwoFactorAuthentication  
Account.Register  
Account.RegisterConfirmation  
Account.ResendEmailConfirmation  
Account.ResetPassword  
Account.ResetPasswordConfirmation

返回到 Visual Studio,现在应该会看到已添加的文件。EnableAuthenticator.cshtml

e2c29d42bee3d3cd552bbc21673ead8a.png

在编辑文件之前,我们需要下载qrcode.js JavaScript库,它将为我们渲染并生成QR码。将其保存在文件夹中。EnableAuthenticator.cshtmlwwwroot\lib

在这里,我将其重命名为qrcodejs

5520531d99b5e793aa006fc6ae37a69d.png

接下来,创建一个新文件,使用以下代码在 中调用它。qr.jswwwroot\js

window.addEventListener("load", () => {  
  const uri = document.getElementById("qrCodeData").getAttribute('data-url');  
  new QRCode(document.getElementById("qrCode"),  
    {  
      text: uri,  
      width: 150,  
      height: 150  
    });  
});

585fed577e3b2d0c312a31ab53c9d598.png

最后,打开文件,然后:EnableAuthenticator.cshtml

  • 更新该部分以添加对以前下载的库的引用。Scriptsqrcode.js

  • 添加带有调用的文件以生成二维码。qr.js

@section Scripts {  
    @await Html.PartialAsync("_ValidationScriptsPartial")  
  
    <script type="text/javascript" src="~/lib/qrcodejs/qrcode.js"></script>  
    <script type="text/javascript" src="~/js/qr.js"></script>  
}

再次运行应用程序,QR 码现在应该出现。

3b9e668d6cd26b40fd643cadb5a96601.png

使用Authenticator应用程序扫描它,它应该可以完美运行。

通过将其添加到身份验证器应用,可以从应用中生成的代码中验证代码。它应该显示 2FA 确认。

5360590054599114f4573f582b679c53.png

现在,尝试注销,然后重新登录。将出现 2FA 表单,提示您输入 2FA 代码。

恭喜,您的 2FA 设置完美。

源代码获取:公众号回复消息【code:19129

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值