如何在 blazor webassembly 项目的服务器端控制器中对用户进行身份验证?

How do I authenticate a user in serverside controller in a blazor webassembly project?

我有一个使用身份服务器和 facebook 身份验证的 blazor webassembly 项目。当从 webassembly 调用一个 api 控制器时,在使用 .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>() 设置一个 httpclient 并将 useridclaimtype 映射到 nameidentifier 之后,一切都很好。用户可通过 UserManager.GetUserAsync.

现在我尝试添加一个纯服务器控制器,我希望用户能够直接从浏览器浏览。但是,当直接浏览到服务器视图时,尽管我进行了设置,但似乎没有身份验证:

app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();

services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();

services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

services.AddAuthentication()
.AddIdentityServerJwt().AddFacebook(facebookOptions =>
{
   facebookOptions.AppId = Configuration["id"];
   facebookOptions.AppSecret = Configuration["idpwd"];
});

services.Configure<IdentityOptions>(options => options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

我曾希望中间件能够通过此设置自动为我进行身份验证。但是即使我先登录并携带一个 .AspNetCore.Identity.Application cookie,用户仍然无法通过 usermanager 访问。我只得到一个没有名字和声明的空白身份,如果我向控制器添加一个 Authorize 属性,我会得到一个 401.

我是不是漏掉了一些秘密成分来让它发挥作用?我对中间件应该为我做什么有错误的期望吗?

更新:

玩了一会儿并比较了一个标准的 asp.net 核心项目和我的项目。似乎这是杀死服务器端控制器的行:

services.AddAuthentication().AddIdentityServerJwt()

如果我不添加 AddIdentityServerJwt(),控制器会正常运行并在我需要授权时重定向我,并正确登录。谁能向我解释为什么会这样? AddIdentityServerJwt() 中间件是否与 returns 视图的控制器不兼容?

在我看来,添加 AddIdentityServerJwt() 会增加以下要求:每个请求都需要具有不记名令牌的授权 header,这是 blazor 客户端 httpclient 提供的。但是,当浏览器直接进行调用时,承载令牌丢失,管道也不会尝试对用户进行身份验证以获取承载令牌。

发生了什么事

AuthorizeFilter on your controller endpoint and the usage of the AddIdentityServerJwt on your authentication builder is probably the culprit here. AddIdentityServerJwt adds the Jwt authentication scheme as the default authentication scheme indicated by the passage of the authentication scheme name in the AddAuthentication method. This means that the handler associated with this Jwt authentication scheme (JwtBearerHandler) will be the class that's emitting the 401 through its HandleChallengeAsync方法的具体混合。请注意,此方法中没有用于进一步操作的重定向,这就是为什么您看不到发生重定向的原因。

为什么会这样?

由于控制器端点上的 AuthorizeFilter,调用了 JwtBearerHandler 上的 HandleChallengeAsync 方法。执行 AuthorizeFilter 的方法是 OnAuthorizationAsync method (which is invoked right after the initialization of the filter). Since there's no policy specified in the Authorization attribute put on your controller (there was nothing passed in as a parameter in [Authorize]), the default authorization policy ends up being used. The default policy basically checks if any of the authentication schemes have successfully authenticated you (by looking at the identities in the HttpContext), and return a response for a challenge if not。此挑战默认为默认身份验证方案的挑战,这就是在 JwtBearerHandler 上调用 HandleChallengeAsync 的原因。

您可以详细了解政策 here, and more about the AuthorizationFilter (and filters in general) here