.Net Core 3.1 合并 JWT 和 OpenIDConnect 身份验证
Posted
技术标签:
【中文标题】.Net Core 3.1 合并 JWT 和 OpenIDConnect 身份验证【英文标题】:.Net Core 3.1 Merging JWT and OpenIDConnect Authentication 【发布时间】:2020-09-23 17:02:53 【问题描述】:短版:我无法在我的 .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));
我还试图让我的 .NET Core 控制台应用程序调用 API(作为守护程序连接)到上述 MVC 网站 (所有都在我的 Microsoft Azure 帐户的应用程序注册部分中配置)。我可以将控制台应用程序连接到 MVC 网站,它会成功地在控制器中命中操作结果,但前提是我在网站的 Startup.cs 的 ConfigureServices 方法中使用以下内容:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddProtectedWebApi("AzureAd", Configuration, options => Configuration.Bind("AzureAD", options));
基本上,如果我只使用 OpenIdConnect 选项,我的网络用户可以访问该网站,但我的控制台应用程序被拒绝。如果我只使用 JwtBearer 选项,那么我的控制台应用程序可以连接,但我的网络用户被拒绝。
我整天都在用 Google-Bing 搜索,我正在努力让这两种配置的混搭发挥作用,而不会将另一个淘汰。
我曾尝试使用 .AddJwtBearer() 方法,但完全被它弄糊涂了:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddSignIn("AzureAd", Configuration, options => Configuration.Bind("AzureAd", options))
.AddJwtBearer(options => Configuration.Bind("AzureAD", options));
这些是如何协同工作的,这样两者都可以就位,我的网络应用程序可以通过浏览器运行,而控制台应用程序(守护程序)也可以工作?我可以将两者都绑定到我的 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"
2020 年 6 月 15 日更新: 在为 AGES 开启/关闭此功能后,我找到了一个合适的解决方案,因此我将奖励积分授予@michael-shterenberg。另外,我现在知道我可以从 @gary-archer 和他令人印象深刻的博客网站中学到很多东西。我只是碰巧从迈克尔的意见中获得了成功。
这是 Startup.cs 文件的修改,位于上图中的 ASP.NET Core MVC Web 应用程序中:
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();
);
);
...再加上在我试图从守护进程应用程序调用的控制器上使用以下属性。
[Authorize("UserAndApp")]
我的用户仍然可以使用 Azure Active Directory 进程登录网站,现在我的自动化进程也可以登录。
如果有人难以理解 Azure 应用注册方面的所有这些工作原理,请尝试这篇真正解释性的博文:
Secure a .NET Core API using Bearer Authentication
(我希望我早先看到这一点,当时我正试图了解 Azure 应用程序注册过程的工作原理!)
【问题讨论】:
使用多种身份验证方案可能是采取的方向:docs.microsoft.com/en-us/aspnet/core/security/authorization/… 如果我理解正确:用户必须是 Authenticated 并且 ConsoleApp 必须是 Authorized ? 【参考方案1】:感觉架构不太对劲,你需要将你的 Web Back End 执行的 2 个角色分开:
当前仅网络架构
您有一个使用身份验证 cookie 的 Web UI 前端 您的 Web 后端需要 cookie 来处理查看请求 您的 Web 后端需要 cookie 来处理 API 请求 您的控制台客户端无法使用 cookie,因此无法调用 API 入口点多客户端架构
您需要更新 Web 后端以包含受 OAuth 2.0 访问令牌而非 cookie 保护的 API 入口点。控制台应用程序将能够调用您的 Web 后端。
.NET CORE 子路径
在您的 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();
);
.NET CORE API 示例
有关使用子路径的示例,请参阅我的Sample .Net Core API。 startup class 是 ASP.Net 中间件与不同子路径的不同身份验证处理连接的地方。
未来的可能性
一旦你有了上述逻辑分离,你就有可能在未来进一步发展它,例如完全没有 cookie 的模型:
仅基于令牌将 API 开发为完全独立的组件 将 Web UI 更新为使用客户端安全库的 SPA我在https://authguidance.com 的博客遵循这种方法,我的示例 API 都支持任何类型的客户端。
【讨论】:
嗨,Gary - 感谢您的建议,虽然我认为它本身就是一个相当简单的架构......我可以将 MVC 站点换成 SPA 站点,但不知道会有多少帮助。我添加了一张图片来进一步解释架构,因为我认为我的解释使它听起来比实际更复杂。 好的 Brett - 感谢您的解释 - 我更新了我的原始答案 - 我认为 UseWhen 功能是您所需要的 感谢您 - 结果一直在阅读您的博客文章,并且可以看到我对 .NET 中的安全性尚不了解的东西感到震惊。【参考方案2】:这是对我有用的解决方案(在 ASP .NET Core 2.1 和 3.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;
);
请注意,我明确没有绑定您的 AD 配置,因为需要将 /common 应用于权限(或租户 ID)
我还将受众和颁发者的验证设置为 false,以便任何 AAD 令牌都可以用于测试。您显然应该设置正确的受众/发行者
我使用了AddAzureAd
而不是AddSignIn
(您使用的是自定义外部库吗?)
-
创建一个接受两种身份验证方案的策略:
services.AddAuthorization(options =>
options.AddPolicy("UserAndApp", builer =>
builer.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
builer.AuthenticationSchemes.Add(AzureADDefaults.AuthenticationScheme);
builer.RequireAuthenticatedUser();
);
);
将此替换为您现有的授权设置
-
在控制器中使用新策略名称:
[Authorize("UserAndApp")]
public class HomeController : Controller
对机制的一些解释:
您不想设置自动身份验证方案,因为这将是授权中间件中运行的默认模式,而您有两种不同的类型
策略将尝试运行两个身份验证处理程序,如果其中一个成功,则身份验证成功
注意:如果您使用无效的 Bearer 令牌发送请求,则两个身份验证处理程序都将失败,在这种情况下,AzureADDefaults
将“获胜”,因为它实际上实现了质询方法并将重定向你(状态码 302),所以一定要在你的应用中处理这个
【讨论】:
顺便说一句,我的代码示例使用了AddJwtBearer
,您也可以使用AddProtectedWebApi
(它只是在后台使用AddJwtBearer
,并提供了一个抽象层以更好地利用AzureAdOptions)
感谢您 - 我已尝试按照您的建议实施,但得到以下结果: InvalidOperationException:没有为方案“AzureAD”注册身份验证处理程序。注册的方案有:Cookies、OpenIdConnect、Bearer。您是否忘记调用 AddAuthentication().Add[SomeAuthHandler]("AzureAD",...)?
我实际上找不到关于您的 AddSignIn
方法的任何文档(我在示例中使用了 AddAzureAD
),看起来它注册了不同的方案名称。 @BrettRigby,您能否尝试将所有 3 个(不含 AzureAd)方案添加到策略中:builer.AuthenticationSchemes.Add("Cookies");
builer.AuthenticationSchemes.Add("OpenIdConnect");
builer.AuthenticationSchemes.Add("Bearer");
以上是关于.Net Core 3.1 合并 JWT 和 OpenIDConnect 身份验证的主要内容,如果未能解决你的问题,请参考以下文章
.NET Core 3.1 基于角色的 JWT 授权返回 403 禁止
NET Core 3.1 MVC 授权/身份验证,带有在单独的 Net Core 3.1 Web Api 中从外部获取的令牌 (JWT)
ASP.NET Core 3.1 MVC JWT 登录返回 401
使用两个 JWT 身份验证方案,ASP.NET Core 3.1