【问题标题】:Changing ASP.NET Core 2.0 Identity Connection String in Controller在控制器中更改 ASP.NET Core 2.0 标识连接字符串
【发布时间】:2018-05-11 00:17:14
【问题描述】:

我正在创建一个基于多租户结构的 Web 应用程序。因此,每个用户请求都会根据主机名将该用户锁定为特定租户(每个租户使用不同的数据库。)我可以设置一般上下文的连接字符串,但我现在不知道如何更改连接ASP.NET Core 2.0 Identity 对象的字符串。

我看到他们让我们这样做的默认方式是在 StartUp.cs 中设置 UseAuthentication()。然后当调用控制器时,DI 会设置 UserManager 和 SignInManager 对象。我假设我需要在每个控制器构造函数中创建新的 UserManager 和 SignInManager 对象,并为该租户传递特定的连接字符串(根本不使用 DI。)

对此有什么想法吗?

澄清:

我希望弄清楚是否可以通过依赖注入将传递的身份对象的数据库连接字符串更改为控制器。

【问题讨论】:

    标签: c# asp.net-core asp.net-core-identity


    【解决方案1】:
    // Startup class of asp.net core 2
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMultitenancy<Tenant, TenantResolver>();
    
        string accountsConnection = configuration.GetConnectionString("AccountsConnectionString");
    
        services.AddScoped<LicenseManager>();
        services.AddScoped<AccountManager>();
    
    
        services.AddEntityFrameworkSqlServer()
            .AddDbContext<AccountsContext>(options =>
            {
                options.UseSqlServer(accountsConnection);
            })
            .AddDbContext<MasterDataContext>(options => options.UseSqlServer(masterdataConnection))
            .AddDbContext<DataContext>();
    
        services.AddMvc();
    }
    
    // Tenantresolver which get's the connectionstring from the claims. (seperate file/class)
    // On authentication i create several claims with one claim with the connectionstring per user. 
    // I use a seperate accounts database where all the tenants are saved which each of them has his own connectionstring
    public class TenantResolver : MemoryCacheTenantResolver<Tenant>
    {
        private readonly AccountsContext Context;
    
        public TenantResolver(AccountsContext context, IMemoryCache cache, ILoggerFactory logger)
            :base(cache, logger)
        {
            Context = context;
        }
    
        protected override string GetContextIdentifier(HttpContext context)
        {
            return context.User.Claims.Where(r => r.Type == ClaimTypes.AccountIdType).Select(s => s.Value).SingleOrDefault();
        }
    
        protected override IEnumerable<string> GetTenantIdentifiers(TenantContext<Tenant> context)
        {
            return new[] { context.Tenant.Tenant_Id.ToString() };
        }
    
        protected override Task<TenantContext<Tenant>> ResolveAsync(HttpContext context)
        {
            TenantContext<Tenant> tenantContext = null;
    
            string claim = context.User.Claims.Where(r => r.Type == ClaimTypes.AccountIdType).Select(s => s.Value).SingleOrDefault();
    
            var tenant = Context.Accounts
                    .Where(q => q.IsActive == true && q.Account_Id.ToString() == claim)
                    .Select(s => new Tenant(s.Account_Id, s.DataStore.DatabaseConnection))
                    .FirstOrDefault();
    
            if (tenant != null)
            {
                tenantContext = new TenantContext<Tenant>(tenant);
            }
    
            return Task.FromResult(tenantContext);
        }
    }
    
    // Tenant class where i save the info needed.
    public class Tenant
    {
        public Tenant(
            Guid tenant_id,
            string database_ConnectionString)
        {
            Tenant_Id = tenant_id;
            Database_ConnectionString = database_ConnectionString;
        }
    
        public Guid Tenant_Id { get; set; }
        public string Database_ConnectionString { get; set; }
    }
    
    // DbContext class which uses the tenant and links the connectionstring to the current tenant.
    public class DataContext : DbContext
    {
        private readonly Tenant Tenant;
    
        public DataContext(DbContextOptions<DataContext> options, Tenant tenant)
            : base(options)
        {
            this.Tenant = tenant;
        }
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (Tenant != null)
            {
                optionsBuilder.UseSqlServer(Tenant.Database_ConnectionString);
            }
    
            base.OnConfiguring(optionsBuilder);
        }
    }
    

    【讨论】:

    • 仅供参考,我使用了一个单独的数据库(帐户)来存储租户及其用户、许可证和设置。 ;)
    • 我看到在这段代码中你会得到一个可以用于特定租户数据库的连接字符串。您将如何将该连接字符串应用到 AccountController 所需的身份对象?
    • 我创建了一个自定义用户对象,它有一个 account_id(与 Tenant_Id 相同)。我使用 OpenIdDict 进行 oauth 身份验证,它具有创建身份验证票证的交换方法。在这张票(索赔)中,我添加了一个带有此 ID 的额外索赔。 (您也可以使用这个地方来保存您的连接字符串,但是您必须使用一种声明访问器将其转换为具有连接字符串的租户)。租户解析器使用此 ID 来解析连接字符串。如果你愿意,我有一个例子。
    • 我认为您的设置可能有点过于自定义或特定于您的需求而不是我想要的(尽管我最终可能会推出更多自定义的东西。)我正在寻找一种改变的方法传递给控制器​​的现有 UserStore 身份对象上的连接字符串。
    • 据我所知,唯一可行的方法是向身份验证票添加自定义声明并在数据上下文中使用它,如上所示。
    猜你喜欢
    • 2016-08-17
    • 2018-05-11
    • 1970-01-01
    • 2019-05-27
    • 1970-01-01
    • 2014-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多