Задача сделать из обычного REST-приложения на ядре asp.net 2.2 - Мультитенант, я сталкивался с такой проблемой: у приложения была авторизация по ролям на основе токенов JWT, но теперь, перед авторизацией, нужно найтикакой арендатор подключен, потому что у каждого арендатора должна быть своя собственная база данных со всеми таблицами, включая пользователей. Арендаторы хранятся в отдельной базе данных. Как можно проверить с каждым запросом, какой арендатор подключается перед авторизацией, чтобы изменить строку подключения к базе данных в контексте и использовать ее для всех запросов этого пользователя. На основе: https://www.codingame.com/playgrounds/5518/multi-tenant-asp-net-core-5---implementing-database-per-tenant-strategy
Мой код контекста
using Forest.TenantProvider;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Forest.Models
{
public class Context:DbContext
{
public DbSet<DutyForCuteMaterial>DutyForCuteMaterials { get; set; }
public DbSet<DutyGluedTimberProduction> dutyGluedTimberProductions { get; set; }
public DbSet<DutyOnDry> dutyOnDries { get; set; }
public DbSet<DutyOnHiating> dutyOnHiatings { get; set; }
public DbSet<DutyOnStarboardKnots> dutyOnStarboardKnots { get; set; }
public DbSet<Invois> Invoises { get; set; }
public DbSet<InvoisForWasteMarket> InvoisForWastes { get; set; }
public DbSet<StockOfCutMaterial> StockOfCutMaterials { get; set; }
public DbSet<StockOfDryLumber> StockOfDryLumbers { get; set; }
public DbSet<StockOfFuelCapsule> StockOfFuelCapsules { get; set; }
public DbSet<StockOfGluedTimber> StockOfGluedTimbers { get; set; }
public DbSet<StockOfSawdust> StockOfSawdusts { get; set; }
public DbSet<StockOfTrimming> StockOfTrimmings { get; set; }
public DbSet<StockWaste> StockWastes { get; set; }
public DbSet<TimberIncome> TimberIncomes { get; set; }
public DbSet<Workers> Workers { get; set; }
public DbSet<Materials> Materials { get; set; }
public DbSet<DutyOnFuelCapsule> DutyOnFuelCapsules { get; set; }
public DbSet<GeneralStock> GeneralStocks { get; set; }
public DbSet<Person> Users { get; set; }
private Tenant _tenant;
private ILogger<Context> _logger;
public Context(ITenantProvider tenantProvider, ILogger<Context> logger)
{
_tenant = tenantProvider.GetTenant(1);//now just takes 1st tennant in db
_logger = logger;
Database.EnsureCreated();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_tenant.DatabaseConnectionString);
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}}
Мой запуск
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Serilog;
namespace Forest
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = AuthOptions.ISSUER,
ValidateAudience = true,
ValidAudience = AuthOptions.AUDIENCE,
ValidateLifetime = true,
IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(),
ValidateIssuerSigningKey = true,
};
});
services.AddTransient<UserResolveService>();
string con = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<TenantContext>(options => options.UseSqlServer(con));
services.AddDbContext<Context>(o => { });
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddMvc();
services.AddTransient<ITenantProvider, DummyTenantProvider>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddSerilog();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseMiddleware<SerilogMiddleware>();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
}
}
}
И мой AuthController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using Newtonsoft.Json;
using System.Security.Claims;
using Forest.JWTAuth;
using Forest.Models;
namespace Forest.Controllers
{
[Route("api/[controller]")]
public class AccountController:Controller
{
Context db;
TenantContext conT;
string a;
public AccountController(TenantContext contextT ,Context context)
{
this.conT = contextT;
this.db = context;
}
[HttpPost]
public async Task Token([FromBody]AuthData data)
{
var username = data.username;
var password = data.userpassword;
var TenantName = data.tenantName;
var identity = GetIdentity(username, password,TenantName);
if (identity == null)
{
Response.StatusCode = 400;
await Response.WriteAsync("Invalid username or password.");
}
var now = DateTime.UtcNow;
var jwt = new JwtSecurityToken(
issuer: AuthOptions.ISSUER,
audience: AuthOptions.AUDIENCE,
notBefore: now,
claims: identity.Claims,
expires: now.Add(TimeSpan.FromMinutes(AuthOptions.LIFETIME)),
signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256));
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
access_token = encodedJwt,
username = identity.Name
};
Response.ContentType = "application/json";
await Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));
}
private ClaimsIdentity GetIdentity(string username, string password,string tenantName)
{
Person person = db.Users.FirstOrDefault(x => x.Login == username && x.Password == password);
if (person != null)
{
var claims = new List<Claim>
{
new Claim(ClaimsIdentity.DefaultNameClaimType, person.Login),
new Claim(ClaimsIdentity.DefaultRoleClaimType, person.Role),
};
ClaimsIdentity claimsIdentity =
new ClaimsIdentity(claims, "Token", ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
return claimsIdentity;
}
return null;
}
}