Asp.net-Identity

如何在多租戶環境中使用 Asp.Net Core Identity

  • January 7, 2018

我有一個具有預設身份處理的工作 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>();

而已!

引用自:https://stackoverflow.com/questions/48133171