.Net Core 2 OpenID Connect 身份验证和多个身份

Posted

技术标签:

【中文标题】.Net Core 2 OpenID Connect 身份验证和多个身份【英文标题】:.Net Core 2 OpenID Connect Authentication and multiple Identities 【发布时间】:2019-03-14 14:14:12 【问题描述】:

我仍在学习身份框架,并且在尝试在我的 .Net Core 2 MVC 应用程序中设置身份验证时迷失了方向。感谢任何建议,因为我什至不确定我在做什么是正确的。


我需要集成 OpenID Connect 身份提供程序以进行身份验证,并使用辅助数据源进行授权。不方便的是,除了名称声明之外,我不能使用来自 OIDC IdP 的任何声明。其余用户声明必须来自辅助数据源(通过自定义UserStoreUser 实体连接到身份框架)。

我正在使用OpenId Connect provider 来处理身份验证。这工作正常,并给了我第一个身份(我只能使用一个声明)。当我需要获取用户的第二个身份、将其添加到主体并将其设置为默认 Identity 时,我的困惑就开始了。第二个Identity 提供所有用户声明,包括角色。

我对身份框架的理解是我应该有一个带有两个身份的ClaimsPrincipal,以便我可以插入身份框架的其余部分。但是,对于两个身份,默认 ClaimsPrincipal 将自动选择第一个身份(这是我不能使用的身份),因此看来我应该创建一个自定义 ClaimsPrincipal 并设置 PrimaryIdentitySelector 以便我的第二个身份是主要的。

public class MyClaimsPrincipal : ClaimsPrincipal

    private static readonly Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity> IdentitySelector = SelectPrimaryIdentity;

    /// <summary>
    /// This method iterates through the collection of ClaimsIdentities and chooses an identity as the primary.
    /// </summary>
    private static ClaimsIdentity SelectPrimaryIdentity(IEnumerable<ClaimsIdentity> identities)
    
        // Find and return the second identity
    

从 OIDC IdP 获得经过验证的令牌后,我会获取第二个身份,创建一个新的 MyClaimsPrincipal,将这两个身份添加到新的主体。在那之后,我不确定如何处理这个新校长。

我尝试通过SignInManager 登录用户,在HTTP 上下文中明确设置用户,并使用中间件将ClaimsPrincipals 转换为MyClaimsPrincipals,但所有这些似乎都无济于事。我想我没有抓住重点。

一些具体问题:

这是最好的方法吗?通常对所有这些都感到困惑,因此很难判断我是否走在正确的轨道上。 创建自定义主体后,如何将其“设置”到 HTTP 上下文中以使其持久化? Cookie 身份验证如何与 OpenId Connect 身份验证配合使用?似乎 OIDC 以某种方式将用户传递给 Cookie 身份验证,并且 OIDC 身份验证需要添加 cookie 身份验证才能工作。

【问题讨论】:

【参考方案1】:

使用 OpenID Connect 方案时要知道的重要一点是,该方案永远不会单独运行。在几乎每一个你能找到的例子中,你都会看到它与 cookie 方案相结合。原因很简单:OIDC 用于通过外部身份验证提供程序对用户进行身份验证。但这种身份验证只是暂时的。为了将其本地存储在您的应用程序中,您需要登录您的用户。这通常通过 cookie 身份验证方案完成(尽管可以通过其他方式完成)。

使用 OIDC 和 cookie 的应用程序的身份验证流程通常如下所示:

    用户访问您的应用程序。 身份验证:cookie 方案,默认身份验证方案,将尝试对用户进行身份验证。如果没有 cookie,处理程序将挑战身份验证。 挑战:OIDC 方案,默认挑战方案,将挑战用户并重定向到外部身份验证提供程序。 用户将通过外部身份验证提供程序进行身份验证,并将被重定向回应用程序。 质询回调:OIDC 方案将从外部身份验证提供程序获取响应,完成质询并创建声明主体。 登录:OIDC 方案将使用其配置的登录方案(cookie 方案)登录该主体。 cookie 方案将登录用户并创建一个持久保存在用户浏览器中的 cookie。 在对您的应用程序的后续请求中,用户将包含一个有效的 cookie,因此 cookie 方案可以成功验证用户,而无需再次挑战 OIDC 方案。

因此假设一切正常,OIDC 方案将不会再次参与身份验证。相反,每次都会使用来自 cookie 的身份。

您可以使用它来扩展 OIDC 方案使用附加声明创建的主体,然后再登录并由 cookie 方案保留。您可以使用位于 OIDC 和 cookie 方案之间的自定义登录方案来执行此操作,或者您可以简单地附加到 OIDC 方案的身份验证事件,质询完成后调用但 em>在登录之前

您可以为此使用TicketReceived event:

public void ConfigureServices(IServiceCollection services)

    // …

    services.AddAuthentication(options =>
    
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    )
        .AddCookie()
        .AddOpenIdConnect(options =>
        
            // …

            options.Events.OnTicketReceived = OnOpenIdConnectTicketReceived;
        );


public static Task OnOpenIdConnectTicketReceived(TicketReceivedContext context)

    if (context.Principal.Identity is ClaimsIdentity identity)
    
        identity.AddClaim(new Claim("foo", "bar"));
    

    return Task.CompletedTask;

【讨论】:

在 OnTokenValidated 中而不是在 OnTicketReceived 中添加额外的声明不是更好吗?我假设在您绝对确定令牌确实有效之后,您会想要进行数据库调用以检索声明的数据。 @Tincan TicketReceived 事件实际上是运行just before the sign-in happens 的最后一件事。所以它在TokenValidated 之后运行,并且在收到所有用户信息之后运行。所以,是的,TicketReceived 实际上是调整已构建并将登录的最终身份的最佳方法。 - 我认为您指的是 MessageReceived 事件,它确实在收到令牌后很早就开始了。跨度> 我不知道这个。谢谢你的澄清。

以上是关于.Net Core 2 OpenID Connect 身份验证和多个身份的主要内容,如果未能解决你的问题,请参考以下文章

将 OpenID Connect 与 .NET Core 3.0 ASP.NET Core API 服务一起使用时出错

.Net Core OpenID Connect 使用第三方提供商

ASP.NET Core OpenID Connect 中的单点注销

在 .net core openid 连接配置中重命名配置文件声明映射

ASP .NET Core OpenID Connect - 在外部登录后发出我自己的 cookie?

使用 .Net Core 中的 PKCE 和 Okta 从 Swagger UI 连接 OpenID