简体   繁体   English

Asp.net core 2.0+ - 多种认证方案(Cookie / Bearer)

[英]Asp.net core 2.0+ - Multiple Authentication Schemes (Cookie / Bearer)

I've been struggling to get multiple authentication schemes working correctly in Asp.net core 2.1.我一直在努力使多个身份验证方案在 Asp.net 核心 2.1 中正常工作。

I am using Identity Server with an implicit flow and OpenIdConnect as the protocol.我使用带有隐式流和 OpenIdConnect 作为协议的 Identity Server。

When authorizing an action or controller with one of the schemes only (eg Cookie or Bearer) the functionality works correctly.当仅使用其中一种方案(例如 Cookie 或 Bearer)授权操作或控制器时,该功能可以正常工作。

Example:例子:

  [Authorize(AuthenticationSchemes = "Cookies")]
  [Route("Cookies")]
  public class BearerAndCookiesController : Controller {

If I however I specify on the Authorize attribute both schemes, then it fails partially.但是,如果我在 Authorize 属性上指定了两种方案,那么它会部分失败。 Bearer works as normal, but when I try to view the page in the browser it attempts to redirect to a local Login page ( http://localhost/Account/Login ). Bearer 正常工作,但是当我尝试在浏览器中查看页面时,它会尝试重定向到本地登录页面 ( http://localhost/Account/Login )。

When I inspect the debug logs of Identity Server nothing is returned, which makes sense as it hasn't attempted to contact the Authority.当我检查 Identity Server 的调试日志时,没有返回任何内容,这是有道理的,因为它没有尝试联系管理局。 However when I look at the debug log of the Test MVC Site both the Bearer and Cookie schemes are challenged:但是,当我查看测试 MVC 站点的调试日志时,Bearer 和 Cookie 方案都受到挑战:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:5002/cookies  
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Route matched with {action = "Get", controller = "BearerAndCookies"}. Executing action MvcClient.Controllers.BearerAndCookiesController.Get (MvcClient)
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes (Bearer, Cookies).
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Cookies was challenged.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action MvcClient.Controllers.BearerAndCookiesController.Get (MvcClient) in 68.1922ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 93.2016ms 302 
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:5002/Account/Login?ReturnUrl=%2Fcookies  
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 30.2532ms 404 
Failed to load resource: the server responded with a status of 404 (Not Found) [http://localhost:5002/Account/Login?ReturnUrl=%2Fcookies]

Does anyone know why this isn't working?有谁知道为什么这不起作用? I'll the person a beer!我要人喝啤酒! It's been hunting me for the last week.上周它一直在追捕我。

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-2.2&tabs=aspnetcore2x https://docs.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-2.2&tabs=aspnetcore2x

Here is my Startup.cs configuration:这是我的 Startup.cs 配置:

   public void ConfigureServices(IServiceCollection services) {

      services.AddMvc();

      JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

      services.AddAuthentication(options => {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
      })
      .AddJwtBearer(options => {
        options.Authority = "http://localhost:5000";
        options.Audience = "myApi";
        options.RequireHttpsMetadata = false;
      })
      .AddCookie("Cookies")
      .AddOpenIdConnect("oidc", options => {
        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;

        options.ClientId = "myApi";
        options.SaveTokens = true;
      });
    }
  [Authorize(AuthenticationSchemes = AuthSchemes)]
  [Route("Cookies")]
  public class BearerAndCookiesController : Controller {

    private const string AuthSchemes =
      JwtBearerDefaults.AuthenticationScheme + "," +
      CookieAuthenticationDefaults.AuthenticationScheme;

I wanted to give a better explanation of this answer:我想更好地解释这个答案:

  1. I had to move services.AddAuthorization after the part were I added both of the schemes.在我添加了两个方案之后,我不得不移动services.AddAuthorization This ensures both schemes are registered correctly.这可确保正确注册两个方案。
 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

      services.AddAuthentication(options => {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
      })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options => {
          options.SignInScheme = "Cookies";
          options.Authority = "http://localhost:5000";
          options.RequireHttpsMetadata = false;
          options.ClientId = "myApi";
          options.SaveTokens = true;
        }).AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, options => {
          options.Authority = "http://localhost:5000";
          options.ApiName = "myApi";
          options.RequireHttpsMetadata = false;
        });

      services.AddAuthorization(options => {
      ...
      });
  1. Then instead of specifying the Authorization Scheme as a part of the Controller Action Authorize tag, I used a global policy when using services.AddAuthorization然后我没有将授权方案指定为控制器操作授权标记的一部分,而是在使用services.AddAuthorization时使用了全局策略
services.AddAuthorization(options =>
{
    var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
        CookieAuthenticationDefaults.AuthenticationScheme,
        JwtBearerDefaults.AuthenticationScheme);
    defaultAuthorizationPolicyBuilder =
        defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
    options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
  1. When I navigated to the any parts of the API it wouldn't redirect to the Login screen.当我导航到 API 的任何部分时,它不会重定向到登录屏幕。 I noticed that if you logged in first by navigating to Identity Server, then go back to that page it would actually authenticate you as normal.我注意到,如果您首先通过导航到 Identity Server 登录,然后返回该页面,它实际上会像往常一样对您进行身份验证。 So I've put in what feel to be a little bit of hack.所以我已经加入了一点点黑客的感觉。 It is important that this directly goes in under the app.UseAuthentication .重要的是,这直接进入app.UseAuthentication 下
  app.UseAuthentication();
      app.Use(async (context, next) => {
        await next();
        var bearerAuth = context.Request.Headers["Authorization"]
                           .FirstOrDefault()?.StartsWith("Bearer ") ?? false;
        if (context.Response.StatusCode == 401
            && !context.User.Identity.IsAuthenticated
            && !bearerAuth) {
          await context.ChallengeAsync("oidc");
        }
      });

Bob's your uncle... and thanks to this post for helping considerably!!鲍勃是你的叔叔……感谢这篇文章的帮助!! oipapio.com/question-1510997 oipapio.com/question-1510997

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM