使用.NET 6開發TodoList應用(填坑1)——實現當前登錄用戶獲取

系列導航及源代碼

需求

在前面的文章里使用.NET 6開發TodoList應用(5)——領域實體創建,我們留了一個坑還沒有填上,就是在數據庫保存的時候,CreateUser和ModifiedUser我們當時填的都是Anonymous,完成認證的功能後,現在我們需要實現在保存數據庫的時候填入當前登陸進行操作的用戶名。

目標

實現當前登陸用戶信息獲取。

原理和思路

原理很簡單,在認證時拿到的Token里,payload中是包含登陸User的部分信息的,作為演示,我們需要想辦法獲取到用戶名信息,並在保存數據時填入相應字段。為了獲取Token中包含的用戶信息,需要用到HttpContextAccessor對象。很顯然,需要一個新的接口和實現。

實現

創建當前用戶獲取接口

Application/Common/Interfaces中添加一個新的接口:

  • ICurrentUserService.cs
namespace TodoList.Application.Common.Interfaces;

public interface ICurrentUserService
{
    string? UserName { get; }
}

這裡我們取的是UserName,是因為在返回的Token中包含UserName的信息,如果需要使用UserId或其他信息,需要在GetClaims中添加:

// 演示了返回用戶名和Role兩個claims
var claims = new List<Claim>
{
    // Claims中包含UserName信息
    new(ClaimTypes.Name, User!.UserName),
    new(JwtRegisteredClaimNames.Iss, _jwtConfiguration.ValidIssuer ?? "TodoListApi"),
    new(JwtRegisteredClaimNames.Aud, _jwtConfiguration.ValidAudience ?? "//localhost:5050")
};

實現接口功能

Api/Services中添加類實現接口:

  • CurrentUserService.cs
using System.Security.Claims;
using TodoList.Application.Common.Interfaces;

namespace TodoList.Api.Services;

public class CurrentUserService : ICurrentUserService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CurrentUserService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    // 通過注入的IHttpContextAccessor獲取`HttpContext.User(ClaimsPrinciple)`中對應的Claims信息
    public string? UserName  => _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.Name);
}

並在Program中添加依賴注入:

  • Program.cs
builder.Services.AddSingleton<ICurrentUserService, CurrentUserService>();

使用功能

接下來我們去修改DbContext,需要先在構造函數中注入:

  • TodoListDbContext.cs
private readonly ICurrentUserService _currentUserService;
public TodoListDbContext(
    DbContextOptions<TodoListDbContext> options,
    IDomainEventService domainEventService,
    ICurrentUserService currentUserService) : base(options)
{
    _domainEventService = domainEventService;
    _currentUserService = currentUserService;
}

SaveChangesAsync方法中修改:

public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new())
{
    foreach (var entry in ChangeTracker.Entries<AuditableEntity>())
    {
        switch (entry.State)
        {
            case EntityState.Added:
                entry.Entity.CreatedBy = _currentUserService.UserName;
                entry.Entity.Created = DateTime.UtcNow;
                break;
            case EntityState.Modified:
                entry.Entity.LastModifiedBy = _currentUserService.UserName;
                entry.Entity.LastModified = DateTime.UtcNow;
                break;
        }
    }
// 省略其他...
}

驗證

啟動Api項目,首先獲取Token,再用獲取到的Token去創建一個新的TodoList:

img

可以看到新創建的TodoList的用戶信息已經獲取到了,為了確保數據存儲到數據庫中,我們去數據庫看一下:

img

總結

在本文中我們實現了如何從請求中獲取當前登陸的用戶信息並保存到數據庫中。