如何在 ASP.net Core 应用程序中同时使用 Bearer 和 Cookie 身份验证?

Uch*_*uku 5 c# asp.net authentication cookies bearer-token

我正在开发一个 ASP.net Core 2.0 应用程序,它使用 IdentityServer4 令牌服务器进行身份验证。我的应用程序有 Razor Pages,我想使用 Cookie 身份验证,它还有一个 API,我想使用 Bearer 令牌。

\n\n

这是我的设置Startup.cs

\n\n
    public void ConfigureServices(IServiceCollection services) {\n//...\n        services.AddAuthentication(options => { options.DefaultChallengeScheme = "oidc"; })\n            .AddIdentityServerAuthentication(JwtBearerDefaults.AuthenticationScheme, options =>\n            {\n                options.Authority = Configuration["OpenIDConnect:Authority"];\n                options.ApiName = Configuration["OpenIDConnect:ApiName"];\n                options.JwtBearerEvents.OnChallenge += async context =>\n                {\n                    context.Response.StatusCode = StatusCodes.Status403Forbidden;\n                    context.HandleResponse();\n                };\n            })\n            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)\n            .AddOpenIdConnect("oidc", options =>\n            {\n                options.SignInScheme = "Cookies";\n                options.Authority = Configuration["OpenIdConnect:Authority"];\n                options.RequireHttpsMetadata = false;\n                options.ClientId = Configuration["OpenIdConnect:ClientId"];\n                options.SaveTokens = true;\n                options.Scope.Add("role");\n                options.TokenValidationParameters = new TokenValidationParameters\n                {\n                    NameClaimType = "name",\n                    RoleClaimType = "role"\n                };\n            });\n\n        services.AddAuthorization(options =>\n        {\n            options.AddPolicy(Constants.Policies.ApiUserWithBearerToken,\n                policy =>\n                {\n                    policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);\n                    policy.AuthenticationSchemes.Remove(CookieAuthenticationDefaults.AuthenticationScheme);\n                    policy.AuthenticationSchemes.Remove("oidc");\n                    policy.RequireClaim("MobileClient", "true");\n                    policy.RequireClaim("ApiUser", "true");\n                });\n        });\n}\n\npublic void Configure(IApplicationBuilder app, IHostingEnvironment env) {\n    //\xe2\x80\xa6\n    //\xe2\x80\xa6\n    app.UseAuthentication();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是我尝试使用不记名令牌保护的 API 端点:

\n\n
[Route("api/[controller]"), Authorize(Policy = "ApiUserWithBearerToken", AuthenticationSchemes = "Bearer"), MobileAppController]\npublic class CatalogController : Controller\n{\n    // \xe2\x80\xa6\n    [HttpGet("current")]\n    public async Task<IActionResult> Current()\n    {\n        // \xe2\x80\xa6\n    }        \n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的问题是 ASP.net 似乎完全忽略了我的承载身份验证方案设置。如果我current使用有效的不记名令牌请求端点,它会返回一个到 IdentityServer 的重定向,要求我登录。

\n\n

这是来自 Kestrel 的消息日志:

\n\n
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]\n      Request starting HTTP/1.1 GET http://localhost:5001/api/Catalog/current\ninfo: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]\n      Authorization failed for user: (null).\ninfo: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]\n      Authorization failed for the request at filter \'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter\'.\ninfo: Microsoft.AspNetCore.Mvc.ChallengeResult[1]\n      Executing ChallengeResult with authentication schemes ().\ninfo: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[12]\n      AuthenticationScheme: oidc was challenged.\ninfo: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]\n      Executed action Pinball.Web.Controllers.CatalogController.Current (Catalog.Web) in 1185.1356ms\ninfo: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]\n      Request finished in 1871.9771ms 302\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我删除默认的质询方案,那么 ASP.net 只会抛出一个异常,说明这是必需的。

\n\n

我在这里缺少什么?

\n

Nik*_*dic 2

我遇到了同样的问题,既然我解决了它,我很高兴提供我的解决方案。当您通过调用services.AddIdentityServer()cookie 添加身份服务器时,默认情况下会添加身份验证,如下所示:https://github.com/IdentityServer/IdentityServer4/blob/75b1a660c3cab2580112e6e0288f3f6bed8189f9/src/Configuration/DependencyInjection/IdentityServerServiceCollectionExtensions.cs

builder
   .AddRequiredPlatformServices()
   .AddCookieAuthentication() // <-- Cookie auth added
   .AddCoreServices()
   .AddDefaultEndpoints()
   .AddPluggableServices()
   .AddValidators()
   .AddResponseGenerators()
   .AddDefaultSecretParsers()
   .AddDefaultSecretValidators();
Run Code Online (Sandbox Code Playgroud)

之后您添加身份验证,services.AddAuthentication()此时您不再需要添加 cookie 身份验证,因为它已经添加了。只需要添加IS认证即可,添加如下代码:

services.AddAuthentication()
    .AddIdentityServerAuthentication(options =>
    {
        options.Authority = Configuration["Settings:Authentication:Authority"];
        options.RequireHttpsMetadata = !CurrentEnvironment.IsDevelopment();

        options.ApiName = Configuration["Settings:Authentication:ApiName"];
        options.ApiSecret = Configuration["Settings:Authentication:ApiSecret"];
     });
Run Code Online (Sandbox Code Playgroud)

你也不需要.AddOpenIdConnect()方法。

现在,请记住,完成此操作后,您将声明两种身份验证方法来服务您的身份验证请求。第一个是 cookie(由 IS 添加),另一个是通过调用添加的 JWT 承载.AddIdentityServerAuthentication()。由于 cookie 是首先添加的,因此当任何请求需要进行身份验证时,它也会首先被调用,因此您需要指定在授权时要使用哪种方法,您可以使用以下方法正确执行此操作:

[Route("api/[controller]"), Authorize(Policy = "ApiUserWithBearerToken", AuthenticationSchemes = "Bearer"), MobileAppController]
Run Code Online (Sandbox Code Playgroud)

我个人使用 IS default 但结果是一样的:

[Route("api/[controller]"), Authorize(Policy = "ApiUserWithBearerToken", AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme), MobileAppController]
Run Code Online (Sandbox Code Playgroud)

有了这个,您应该拥有一个可以同时服务 API 请求和 Razor 页面的工作应用程序。