加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-core – openid connect – 在登录期间识别租户

发布时间:2020-12-16 03:35:45 所属栏目:asp.Net 来源:网络整理
导读:我有一个多租户(单一数据库)应用程序,允许跨不同租户使用相同的用户名/电子邮件. 在登录时(隐含流程)我如何识别租户?我想到了以下可能性: 在注册时询问用户帐户slug(公司/租户slug),并在登录期间用户应提供slug以及用户名和密码. 但是在open id请求中没有
我有一个多租户(单一数据库)应用程序,允许跨不同租户使用相同的用户名/电子邮件.

在登录时(隐含流程)我如何识别租户?我想到了以下可能性:

>在注册时询问用户帐户slug(公司/租户slug),并在登录期间用户应提供slug以及用户名和密码.

但是在open id请求中没有参数来发送slug.
>在注册时创建OAuth应用程序并将slug用作client_id.在登录时传递slug在client_id中,我将用它来获取租户ID并继续进行验证用户.

这种做法好吗?

编辑:

也尝试制作路线param的slug部分

.EnableTokenEndpoint("/connect/{slug}/token");

但openiddict不支持这一点.

解决方法

McGuire建议的方法将与OpenIddict一起使用(您可以通过OpenIdConnectRequest.AcrValues访问acr_values属性)但它不是推荐的选项(从安全角度看它并不理想:因为发行者对所有租户来说都是相同的,所以他们最终共享相同的签名密钥).

相反,考虑为每个租户运行一个发行者.为此,您至少有两个选择:

>尝试OrchardCore’s OpenID module:它基于OpenIddict,本机支持多租户.它仍处于测试阶段,但它正在积极开发中.
>覆盖OpenIddict使用的选项监视器以使用每个租户选项.

以下是第二个选项的简化示例,使用自定义监视器和基于路径的租户解析:

实施您的租户解析逻辑.例如:

public class TenantProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public TenantProvider(IHttpContextAccessor httpContextAccessor)
        => _httpContextAccessor = httpContextAccessor;

    public string GetCurrentTenant()
    {
        // This sample uses the path base as the tenant.
        // You can replace that by your own logic.
        string tenant = _httpContextAccessor.HttpContext.Request.PathBase;
        if (string.IsNullOrEmpty(tenant))
        {
            tenant = "default";
        }

        return tenant;
    }
}
public void Configure(IApplicationBuilder app)
{
    app.Use(next => context =>
    {
        // This snippet uses a hardcoded resolution logic.
        // In a real world app,you'd want to customize that.
        if (context.Request.Path.StartsWithSegments("/fabrikam",out PathString path))
        {
            context.Request.PathBase = "/fabrikam";
            context.Request.Path = path;
        }

        return next(context);
    });

    app.UseAuthentication();

    app.UseMvc();
}

实现自定义IOptionsMonitor< OpenIddictServerOptions>:

public class OpenIddictServerOptionsProvider : IOptionsMonitor<OpenIddictServerOptions>
{
    private readonly ConcurrentDictionary<(string name,string tenant),Lazy<OpenIddictServerOptions>> _cache;
    private readonly IOptionsFactory<OpenIddictServerOptions> _optionsFactory;
    private readonly TenantProvider _tenantProvider;

    public OpenIddictServerOptionsProvider(
        IOptionsFactory<OpenIddictServerOptions> optionsFactory,TenantProvider tenantProvider)
    {
        _cache = new ConcurrentDictionary<(string,string),Lazy<OpenIddictServerOptions>>();
        _optionsFactory = optionsFactory;
        _tenantProvider = tenantProvider;
    }

    public OpenIddictServerOptions CurrentValue => Get(Options.DefaultName);

    public OpenIddictServerOptions Get(string name)
    {
        var tenant = _tenantProvider.GetCurrentTenant();

        Lazy<OpenIddictServerOptions> Create() => new Lazy<OpenIddictServerOptions>(() => _optionsFactory.Create(name));
        return _cache.GetOrAdd((name,tenant),_ => Create()).Value;
    }

    public IDisposable OnChange(Action<OpenIddictServerOptions,string> listener) => null;
}

实现自定义IConfigureNamedOptions< OpenIddictServerOptions>:

public class OpenIddictServerOptionsInitializer : IConfigureNamedOptions<OpenIddictServerOptions>
{
    private readonly IDataProtectionProvider _dataProtectionProvider;
    private readonly TenantProvider _tenantProvider;

    public OpenIddictServerOptionsInitializer(
        IDataProtectionProvider dataProtectionProvider,TenantProvider tenantProvider)
    {
        _dataProtectionProvider = dataProtectionProvider;
        _tenantProvider = tenantProvider;
    }

    public void Configure(string name,OpenIddictServerOptions options) => Configure(options);

    public void Configure(OpenIddictServerOptions options)
    {
        var tenant = _tenantProvider.GetCurrentTenant();

        // Create a tenant-specific data protection provider to ensure authorization codes,// access tokens and refresh tokens can't be read/decrypted by the other tenants.
        options.DataProtectionProvider = _dataProtectionProvider.CreateProtector(tenant);

        // Other tenant-specific options can be registered here.
    }
}

在DI容器中注册服务:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    // Register the OpenIddict services.
    services.AddOpenIddict()
        .AddCore(options =>
        {
            // Register the Entity Framework stores.
            options.UseEntityFrameworkCore()
                   .UseDbContext<ApplicationDbContext>();
        })

        .AddServer(options =>
        {
            // Register the ASP.NET Core MVC binder used by OpenIddict.
            // Note: if you don't call this method,you won't be able to
            // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
            options.UseMvc();

            // Note: the following options are registered globally and will be applicable
            // to all the tenants. They can be overridden from OpenIddictServerOptionsInitializer.
            options.AllowAuthorizationCodeFlow();

            options.EnableAuthorizationEndpoint("/connect/authorize")
                   .EnableTokenEndpoint("/connect/token");

            options.DisableHttpsRequirement();
        });

    services.AddSingleton<TenantProvider>();
    services.AddSingleton<IOptionsMonitor<OpenIddictServerOptions>,OpenIddictServerOptionsProvider>();
    services.AddSingleton<IConfigureOptions<OpenIddictServerOptions>,OpenIddictServerOptionsInitializer>();
}

要确认这是否正常,请导航到http://localhost:[port]/fabrikam/.well-known/openid-configuration(您应该使用OpenID Connect元数据获得JSON响应).

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读