.Net Core 3.1 合并 JWT 和 OpenIDConnect 身份验证

Bre*_*gby 6 c# authentication jwt openid-connect .net-core

简短版本:我在我的 .NET Core MVC 网站中合并正确的身份验证配置以允许我的用户针对 Azure Active Directory 进行身份验证时遇到问题,但也允许守护程序连接(来自控制台应用程序)。

长版: 我有一个 .NET Core MVC 网站,当在 Startup.cs 的 ConfigureServices 方法中使用以下内容时,它可以完美地对 Azure Active Directory 进行身份验证:

 services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddSignIn("AzureAd", Configuration, options => Configuration.Bind("AzureAd", options));
Run Code Online (Sandbox Code Playgroud)

我还试图让我的 .NET Core 控制台应用程序将 API(作为守护程序连接)调用到上述 MVC 网站(所有这些都在我的 Microsoft Azure 帐户的应用程序注册部分中配置)。我可以将控制台应用程序连接到 MVC 网站,它会在控制器中成功命中一个操作结果,但前提是我在网站的 Startup.cs 的 ConfigureServices 方法中使用以下内容:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
   .AddProtectedWebApi("AzureAd", Configuration, options => Configuration.Bind("AzureAD", options));
Run Code Online (Sandbox Code Playgroud)

这是架构图...

基本上,如果我只使用 OpenIdConnect 选项,我的网络用户可以访问该网站,但我的控制台应用程序被拒绝。如果我只使用 JwtBearer 选项,那么我的控制台应用程序可以连接,但我的网络用户被拒绝。

我一整天都在使用 Google-Bing,我正在努力让这两种配置的混搭能够正常工作,而不会将另一种淘汰。

我曾尝试使用 .AddJwtBearer() 方法,但我完全被它弄糊涂了:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
   .AddSignIn("AzureAd", Configuration, options => Configuration.Bind("AzureAd", options))
   .AddJwtBearer(options => Configuration.Bind("AzureAD", options));
Run Code Online (Sandbox Code Playgroud)

这些是如何协同工作的,这样两者都可以就位,我的 Web 应用程序可以通过浏览器运行,并且控制台应用程序(守护程序)也可以运行?我可以将两者都绑定到我的 appsettings.json 文件吗??

顺便说一下,appsettings.json 文件如下所示:

{
  "AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "zzzzzzzzzzzzzz.onmicrosoft.com",
  "TenantId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
  "ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "CallbackPath": "/signin-oidc",
  "SignedOutCallbackPath ": "/signout-callback-oidc",
  "ClientSecret": "myAzureClientSecret"
  }
}
Run Code Online (Sandbox Code Playgroud)

2020 年 6 月 15 日更新:为 AGES 开启/关闭此功能后,我找到了一个合适的解决方案,因此我将赏金点数授予 @michael-shterenberg。另外,我现在知道我可以从@gary-archer 和他令人印象深刻的博客网站学到很多东西。我碰巧从迈克尔的意见中获得了成功。

这是上图中 ASP.NET Core MVC Web 应用程序中 Startup.cs 文件的 mods:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddSignIn("AzureAd", Configuration, options =>     
          Configuration.Bind("AzureAd", options))
            .AddJwtBearer(o =>
            {
                o.Authority = "https://login.microsoftonline.com/common";
                o.TokenValidationParameters.ValidateAudience = false;
                o.TokenValidationParameters.ValidateIssuer = false;
            });

services.AddAuthorization(options =>
    {
        options.AddPolicy("UserAndApp", builder =>
        {                    
             builder.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);

builder.AuthenticationSchemes.Add(OpenIdConnectDefaults.AuthenticationScheme);

             builder.RequireAuthenticatedUser();
        });
    });
Run Code Online (Sandbox Code Playgroud)

...再加上我试图从守护程序应用程序调用的控制器上的以下属性的使用。

[Authorize("UserAndApp")]
Run Code Online (Sandbox Code Playgroud)

我的用户仍然可以使用 Azure Active Directory 进程登录网站,现在我的自动化进程也可以登录。

如果有人在努力理解所有这些的 Azure 应用注册方面是如何工作的,请尝试这篇真正解释性的博客文章:

使用承载身份验证保护 .NET Core API

(当我试图了解 Azure 应用程序注册过程的工作原理时,我希望我早点看到它!)

Mic*_*erg 5

这是对我有用的解决方案(在 ASP .NET Core 2.1 和 3.1 上测试)

  1. 不要设置默认身份验证方案,因为您有 2 种类型(Cookie 和 JWT)。即您的调用AddAuthentication应该不带参数:
services.AddAuthentication()
   .AddAzureAD(options => Configuration.Bind("AzureAd", options))
   .AddJwtBearer(o=> {
       o.Authority = "https://login.microsoftonline.com/common";
       o.TokenValidationParameters.ValidateAudience = false;
       o.TokenValidationParameters.ValidateIssuer = false;
});
Run Code Online (Sandbox Code Playgroud)
  • 请注意,我明确没有绑定您的 AD 配置,因为需要将/common应用于权限(或租户 ID)
  • 此外,我将受众和发行者的验证设置为 false,以便任何 AAD 令牌都可以用于测试。您显然应该设置正确的受众/发行者
  • 我使用过AddAzureAd但没有AddSignIn(这是您正在使用的自定义外部库吗?)

    1. 创建接受两种身份验证方案的策略:
services.AddAuthorization(options =>
{
    options.AddPolicy("UserAndApp", builer =>
    {
        builer.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
        builer.AuthenticationSchemes.Add(AzureADDefaults.AuthenticationScheme);

        builer.RequireAuthenticatedUser();
    });
});
Run Code Online (Sandbox Code Playgroud)
  • 将其替换为您现有的授权设置
    1. 在控制器中使用新的策略名称:
[Authorize("UserAndApp")]
public class HomeController : Controller
Run Code Online (Sandbox Code Playgroud)

关于机制的一些解释:

您不想设置自动身份验证方案,因为这将是授权中间件中运行的默认架构,而您有两种不同的类型

该策略将尝试运行两个身份验证处理程序,如果其中一个成功,则身份验证成功

注意:如果您发送带有无效承载令牌的请求,则两个身份验证处理程序都将失败,在这种情况下,它们AzureADDefaults将“获胜”,因为它实际上实现了质询方法并将重定向您(状态代码 302),因此请确保处理此问题在你的应用程序中


Gar*_*her -1

感觉架构不太正确,您需要将 Web 后端执行的 2 个角色分开:

当前的纯网络架构

  • 您有一个使用身份验证 cookie 的 Web UI 前端
  • 您有一个 Web 后端,需要 cookie 来查看请求
  • 您有一个 Web 后端,需要 cookie 来处理 API 请求
  • 您的控制台客户端无法使用 cookie,因此无法调用 API 入口点

多客户端架构

您需要更新 Web 后端以包含由 OAuth 2.0 访问令牌(而不是 cookie)保护的 API 入口点。然后,控制台应用程序将能够调用您的网络后端。

.NET 核心子路径

在您的 Web 后端引入额外的 /api 子路径。UseWhen功能将允许您在不影响其他 Web 后端行为的情况下执行此操作:

/*
 * Apply API behaviour to only subpaths, without impacting the rest of the app
 */ 
app.UseWhen(
ctx => ctx.Request.Path.StartsWithSegments(new PathString("/api")),
api => {
  api.useAuthentication();
  api.useJwtBearer();
});
Run Code Online (Sandbox Code Playgroud)

.NET Core API 示例

有关使用子路径的示例,请参阅我的示例 .Net Core API。启动是 ASP.Net 中间件与不同子路径的不同身份验证处理连接的地方。

未来的可能性

一旦您进行了上述逻辑分离,您将来就有可能进一步发展它,例如完全无 cookie 的模型:

  • 仅基于代币将 API 开发为完全独立的组件
  • 将 Web UI 更新为使用客户端安全库的 SPA

我的博客https://authguidance.com遵循这种方法,并且我的示例 API 都支持任何类型的客户端。