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

时间:2018-03-17 06:35:58

标签: c# asp.net authentication cookies bearer-token

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

这是我Startup.cs中的设置:

    public void ConfigureServices(IServiceCollection services) {
//...
        services.AddAuthentication(options => { options.DefaultChallengeScheme = "oidc"; })
            .AddIdentityServerAuthentication(JwtBearerDefaults.AuthenticationScheme, options =>
            {
                options.Authority = Configuration["OpenIDConnect:Authority"];
                options.ApiName = Configuration["OpenIDConnect:ApiName"];
                options.JwtBearerEvents.OnChallenge += async context =>
                {
                    context.Response.StatusCode = StatusCodes.Status403Forbidden;
                    context.HandleResponse();
                };
            })
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddOpenIdConnect("oidc", options =>
            {
                options.SignInScheme = "Cookies";
                options.Authority = Configuration["OpenIdConnect:Authority"];
                options.RequireHttpsMetadata = false;
                options.ClientId = Configuration["OpenIdConnect:ClientId"];
                options.SaveTokens = true;
                options.Scope.Add("role");
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    RoleClaimType = "role"
                };
            });

        services.AddAuthorization(options =>
        {
            options.AddPolicy(Constants.Policies.ApiUserWithBearerToken,
                policy =>
                {
                    policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
                    policy.AuthenticationSchemes.Remove(CookieAuthenticationDefaults.AuthenticationScheme);
                    policy.AuthenticationSchemes.Remove("oidc");
                    policy.RequireClaim("MobileClient", "true");
                    policy.RequireClaim("ApiUser", "true");
                });
        });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
    //…
    //…
    app.UseAuthentication();
}

这是我尝试使用承载令牌保护的API端点:

[Route("api/[controller]"), Authorize(Policy = "ApiUserWithBearerToken", AuthenticationSchemes = "Bearer"), MobileAppController]
public class CatalogController : Controller
{
    // …
    [HttpGet("current")]
    public async Task<IActionResult> Current()
    {
        // …
    }        
}

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

这是来自Kestrel的消息日志:

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

如果我删除了默认的质询方案,那么ASP.net会抛出一个例外情况,说明它是必需的。

我在这里缺少什么?

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题,自从我解决了问题,我很高兴提供自己的解决方案。 当您通过调用services.AddIdentityServer()添加Identity Server时,默认情况下会添加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();

之后,您添加带有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"];
     });

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

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

[Route("api/[controller]"), Authorize(Policy = "ApiUserWithBearerToken", AuthenticationSchemes = "Bearer"), MobileAppController]

我个人使用IS默认值,但结果是相同的:

[Route("api/[controller]"), Authorize(Policy = "ApiUserWithBearerToken", AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme), MobileAppController]

有了这个,您应该有一个可以同时处理API请求和Razor页面的应用程序。