Asp.net-Identity
如何在多租戶環境中使用 Asp.Net Core Identity
我有一個具有預設身份處理的工作 Asp.Net Core 應用程序。現在我想將它用於多個域。我用 DomainId 擴展了 ApplicationUser。我如何不僅可以處理使用者名/電子郵件來驗證/註冊使用者,還可以處理目前的 DomainId?
在使用者註冊、登錄系統時獲取目前的 DomainId 不是問題,我有一個工作的多租戶 Asp.Net Core 系統。我只對使用 DomainId 的使用者管理有問題。
有什麼設置嗎?我應該重寫什麼來獲得這個功能?例如 UserStore、UserManager?
我找到了一些舊 Asp.Net Identity 的教程,例如:https ://www.scottbrady91.com/ASPNET-Identity/Quick-and-Easy-ASPNET-Identity-Multitenancy 但我找不到任何新的教程Asp.Net 核心標識。
最後我想通了。所以首先,我必須將使用者電子郵件設置為不唯一。旁注:我也在使用電子郵件作為使用者名,我不喜歡向使用者詢問使用者名:
services.Configure<IdentityOptions>(options => { options.User.RequireUniqueEmail = false; });當新使用者註冊自己時,我將目前域 ID 合併到使用者名,這有助於使用者通過完全不同的域將相同的電子郵件/使用者名註冊到系統中。
然後我必須創建我的自定義 UserManager,我在其中覆蓋 FindByEmail:
using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MultiShop.Core.Repositories.User; using MultiShop.Core.Tenant; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Test { public class MyShopUserManager<TUser> : UserManager<TUser>, IDisposable where TUser : class { private readonly ITenantService tenantService; private readonly IUserRepository userRepository; public MyUserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<TUser> passwordHasher, IEnumerable<IUserValidator<TUser> userValidators, IEnumerable<IPasswordValidator<TUser> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<TUser> logger, ITenantService tenantService, IUserRepository userRepository) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) { this.tenantService = tenantService; this.userRepository = userRepository; } public override async Task<TUser> FindByEmailAsync(string email) { ThrowIfDisposed(); if (email == null) { throw new ArgumentNullException(nameof(email)); } var users = (await userRepository.GetAllAsync()).Where(u => u.Email == email); if (users == null) { return null; } if (users.Count() == 1) { return await Store.FindByIdAsync(users.First().Id.ToString(), CancellationToken); } var currentDomain = tenantService.GetCurrentDomainAsync().Result; var user = users.SingleOrDefault(u => u.DomainId == currentDomain.Id); if (user == null) { return null; } return await Store.FindByIdAsync(user.Id.ToString(), CancellationToken); } } }請注意,由於多域和生成的使用者名,您應該使用 userManager.FindByEmailAsync,而不是 FindByNameAsync。
我必須創建自定義 SignInManager 來處理多域使用者:
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MultiShop.Core.Tenant; using MultiShop.Data.Entities; using System.Threading.Tasks; namespace Test { public class MySignInManager : SignInManager<ApplicationUser> { private readonly ITenantService tenantService; public MySignInManager(UserManager<ApplicationUser> userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor, ILogger<SignInManager<ApplicationUser> logger, IAuthenticationSchemeProvider schemes, ITenantService tenantService) : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes) { this.tenantService = tenantService; } public override async Task<SignInResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure) { var currentDomain = await tenantService.GetCurrentDomainAsync(); return await base.PasswordSignInAsync($"{userName}{currentDomain.Id}", password, isPersistent, lockoutOnFailure); } } }最後,我必須將我的自定義管理器註冊到 Asp.Net Core Identity DI:
services .AddIdentity<ApplicationUser, ApplicationRole>() .AddEntityFrameworkStores<MultiShopDbContext>() .AddDefaultTokenProviders() //my custom managers for domain segmented users .AddUserManager<MyUserManager<ApplicationUser>() .AddSignInManager<MySignInManager>();而已!