【问题标题】:How do I setup multiple auth schemes in ASP.NET Core 2.0?如何在 ASP.NET Core 2.0 中设置多个身份验证方案?
【发布时间】:2018-01-23 12:34:16
【问题描述】:

我正在尝试将我的身份验证内容迁移到 Core 2.0,但在使用我自己的身份验证方案时遇到了问题。我在启动时的服务设置如下所示:

var authenticationBuilder = services.AddAuthentication(options =>
{
    options.AddScheme("myauth", builder =>
    {
        builder.HandlerType = typeof(CookieAuthenticationHandler);
    });
})
    .AddCookie();

我在控制器中的登录代码如下所示:

var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, user.Name)
};

var props = new AuthenticationProperties
{
    IsPersistent = persistCookie,
    ExpiresUtc = DateTime.UtcNow.AddYears(1)
};

var id = new ClaimsIdentity(claims);
await HttpContext.SignInAsync("myauth", new ClaimsPrincipal(id), props);

但是当我在控制器或动作过滤器中时,我只有一个身份,而且不是经过身份验证的身份:

var identity = context.HttpContext.User.Identities.SingleOrDefault(x => x.AuthenticationType == "myauth");

浏览这些更改很困难,但我猜我做错了 .AddScheme。有什么建议吗?

编辑:这是(本质上)一个干净的应用程序,它不会在 User.Identies 上产生两组身份:

namespace WebApplication1.Controllers
{
    public class Testy : Controller
    {
        public IActionResult Index()
        {
            var i = HttpContext.User.Identities;
            return Content("index");
        }

        public async Task<IActionResult> In1()
        {
            var claims = new List<Claim> { new Claim(ClaimTypes.Name, "In1 name") };
            var props = new AuthenticationProperties  { IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddYears(1) };
            var id = new ClaimsIdentity(claims);
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id), props);
            return Content("In1");
        }

        public async Task<IActionResult> In2()
        {
            var claims = new List<Claim> { new Claim(ClaimTypes.Name, "a2 name") };
            var props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddYears(1) };
            var id = new ClaimsIdentity(claims);
            await HttpContext.SignInAsync("a2", new ClaimsPrincipal(id), props);
            return Content("In2");
        }

        public async Task<IActionResult> Out1()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return Content("Out1");
        }

        public async Task<IActionResult> Out2()
        {
            await HttpContext.SignOutAsync("a2");
            return Content("Out2");
        }
    }
}

和启动:

namespace WebApplication1
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                })
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie("a2");

            services.AddMvc();
        }

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

            app.UseMvc(routes =>
            {
                routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

【问题讨论】:

标签: c# asp.net asp.net-mvc asp.net-core asp.net-core-2.0


【解决方案1】:

2019 年 12 月的编辑:请先考虑这个答案: Use multiple JWT Bearer Authentication

我的旧答案(不适合使用多个 JWT,而只适合使用 JWT + API 密钥,正如用户评论的那样):

另一种可能性是在运行时确定要选择哪种身份验证策略方案,我曾遇到过可以拥有 http 身份验证承载令牌标头或 cookie 的情况。

所以,感谢https://github.com/aspnet/Security/issues/1469

请求标头中有 JWT 令牌,然后是 OpenIdConnect (Azure AD) 或其他任何内容。

public void ConfigureServices(IServiceCollection services)
    {
        // Add CORS
        services.AddCors();

        // Add authentication before adding MVC
        // Add JWT and Azure AD (that uses OpenIdConnect) and cookies.
        // Use a smart policy scheme to choose the correct authentication scheme at runtime
        services
            .AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = "smart";
                sharedOptions.DefaultChallengeScheme = "smart";
            })
            .AddPolicyScheme("smart", "Authorization Bearer or OIDC", options =>
            {
                options.ForwardDefaultSelector = context =>
                {
                    var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
                    if (authHeader?.StartsWith("Bearer ") == true)
                    {
                        return JwtBearerDefaults.AuthenticationScheme;
                    }
                    return OpenIdConnectDefaults.AuthenticationScheme;
                };
            })
            .AddJwtBearer(o =>
            {
                o.Authority = Configuration["JWT:Authentication:Authority"];
                o.Audience = Configuration["JWT:Authentication:ClientId"];
                o.SaveToken = true;
            })
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddAzureAd(options => Configuration.Bind("AzureAd", options));

        services
            .AddMvc(config =>
            {
                var policy = new AuthorizationPolicyBuilder()
                                 .RequireAuthenticatedUser()
                                 .Build();
                // Authentication is required by default
                config.Filters.Add(new AuthorizeFilter(policy));
                config.RespectBrowserAcceptHeader = true;
            });
            
            ...
            
            }

编辑于 07/2019:我必须添加一个指向以下提案的链接,因为它也很有帮助:您可能不会像我一样在 AddAuthentication() 中使用参数,因为这会设置默认方案。一切都在这里得到很好的解释: Use multiple JWT Bearer Authentication。 我真的很喜欢这种另一种方法!

【讨论】:

  • 我尝试了上面的代码,但收到错误Process is terminating due to StackOverflowException (DotNet Core 2.2)。如果提供了令牌,我想使用 JWT,否则 openid 连接。
  • 这种方法还不错,发生的情况是两种身份验证机制总是被触发,实际上你引用的url以更好的方式面对这个,实际上我找不到一种方法来选择性地通过在 Authorize 属性中指定 Scheme 似乎被忽略了,我实现它的唯一方法是在 [Authorize] 中指定策略,并且在策略定义中指定 Scheme。
  • 您确定两个处理程序都使用此配置运行吗?我已经尝试实现上述内容,并且仅在我的情况下运行相关的处理程序。另一方面,链接的解决方案每次都运行。奇怪的。也许是因为它们都是 JWT,而我用它来解决使用 Bearer 或 API 密钥的问题。
  • 感谢@barbara.post!
  • 芭芭拉,你的技术很好,即使有参数。 @john-reilly 在他的“使用 ASP.NET Core 的双启动身份验证”博文中解释了如何:blog.johnnyreilly.com/2020/03/…
【解决方案2】:

浏览这些更改很困难,但我猜我做错了 .AddScheme。

不要使用AddScheme:它是为处理程序编写者设计的低级方法。

如何在 ASP.NET Core 2.0 中设置多个身份验证方案?

要注册 cookie 处理程序,只需执行以下操作:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "myauth1";
        })

       .AddCookie("myauth1");
       .AddCookie("myauth2");
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseAuthentication();

        // ...
    }
}

重要的是要注意,您不能像在 1.x 中那样注册多个默认方案(这个巨大重构的重点是避免同时拥有多个自动身份验证中间件)。

如果您绝对需要在 2.0 中模拟这种行为,您可以编写一个自定义中间件,手动调用 AuthenticateAsync() 并创建一个包含您需要的所有身份的 ClaimsPrincipal

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "myauth1";
        })

       .AddCookie("myauth1");
       .AddCookie("myauth2");
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseAuthentication();

        app.Use(async (context, next) =>
        {
            var principal = new ClaimsPrincipal();

            var result1 = await context.AuthenticateAsync("myauth1");
            if (result1?.Principal != null)
            {
                principal.AddIdentities(result1.Principal.Identities);
            }

            var result2 = await context.AuthenticateAsync("myauth2");
            if (result2?.Principal != null)
            {
                principal.AddIdentities(result2.Principal.Identities);
            }

            context.User = principal;

            await next();
        });

        // ...
    }
}

【讨论】:

  • 我建立了一个干净的项目并使用了上面的代码,然后创建了登录方法(类似于我的问题中的代码),但它不起作用。 User.Identities 上只有一个身份,它是与默认值关联的身份。另外,奇怪的是,它的 IsAuthenticated 属性是假的。
  • 如果没有看到完整的应用程序,很难说出问题所在。考虑更新您的问题以包含详细的 ASP.NET Core 日志。这肯定会有所帮助。
  • 我在上面添加了代码......这就是整个应用程序。如果我调试,然后点击 /testy/in1 然后点击 /testy/in2,然后返回 /testy 并在 Index() 的第一行放置一个断点,只有一个标识。不确定您想查看哪些日志。
  • 正如我所说,这是现在 2.0 中的预期行为,每个应用程序只能有一个默认处理程序(因此,每个请求只有一个身份)。考虑使用[Authorize(AuthenticationSchemes = "myauth1")][Authorize(AuthenticationSchemes = "myauth2")] 装饰您的操作,以便为每个操作选择正确的身份。
  • 答案已更新以包含自定义中间件示例。
【解决方案3】:

https://stackoverflow.com/a/51897159/4425154 的解决方案有帮助。在提到的解决方案之上要考虑几个项目,

  1. 确保您使用的是 .net core run-time 2.1 或更高版本
  2. 如果您使用中间件,请确保您的授权策略如下所述

       services.AddMvc(options =>
        {
            var defaultPolicy = new AuthorizationPolicyBuilder(new[] { CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme })
                      .RequireAuthenticatedUser()
                      .Build();
            options.Filters.Add(new AuthorizeFilter(defaultPolicy));
        })
    

【讨论】:

  • 在 dotnet 核心上进行设置的方法多么复杂。天哪.. 谢谢你的回答。
【解决方案4】:

如果有人需要解决方案,这就是我所做的:

services.AddMvc(options =>
{
            
     var defaultPolicy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes(IdentityServerAuthenticationDefaults.AuthenticationScheme, BasicAuthenticationDefaults.AuthenticationScheme)
         .RequireAuthenticatedUser()
         .Build();

      options.Filters.Add(new AuthorizeFilter(defaultPolicy));
});

services.AddAuthentication()
    .AddIdentityServerAuthentication(option config here)
    .AddBasicAuthentication(setting);

【讨论】:

    【解决方案5】:
    //******Startup=>ConfigureServices******
    
    services.AddAuthentication(option =>
    {
        option.DefaultScheme = "AdministratorAuth";
    })
    .AddCookie("AdministratorAuth", "AdministratorAuth", option =>
    {
        option.Cookie.Name = "AdministratorAuth";
        option.LoginPath = new PathString("/AdminPanel/Login");
        option.ExpireTimeSpan = TimeSpan.FromMinutes(14400);
        option.AccessDeniedPath = "/Error/UnAuthorized";
        option.LogoutPath = "/Security/Logout";
    })
    .AddCookie("UsersAuth", "UsersAuth", option =>
    {
        option.Cookie.Name = "UsersAuth";
        option.LoginPath = new PathString("/Security/LoginUser/");
        option.ExpireTimeSpan = TimeSpan.FromMinutes(144000);
        option.AccessDeniedPath = "/Error/UnAuthorized";
        option.LogoutPath = "/Security/LogoutUser";
    });
        
    //______________________________________________________________
        
    //******Startup=> Configure******
        app.UseAuthentication();
        app.UseCookiePolicy();
    
    //______________________________________________________________
        
    //******Admin Login******
        var status = HttpContext.SignInAsync("AdministratorAuth", new ClaimsPrincipal(principal), properties)IsCompleted;
        
    //******OtherUsers Login******
        var status = HttpContext.SignInAsync("UsersAuth", new ClaimsPrincipal(principal), properties)IsCompleted;
        
    //______________________________________________________________
        
    [Authorize(AuthenticationSchemes = "AdministratorAuth")]
    public class DashboardController : BaseController
    {
    
    }
    

    【讨论】:

      【解决方案6】:

      扩展@HotN 解决方案 如果将 Blazor 服务器 与 AddDefaultIdentity 和 Blazor Wasm JwtBearer

      一起使用
          services.AddAuthentication(opt =>
          {
              opt.DefaultAuthenticateScheme = "smart";
              opt.DefaultChallengeScheme = "smart";
          })
          .AddPolicyScheme("smart", "Authorization Bearer or OIDC", options =>
          {
              options.ForwardDefaultSelector = context =>
              {
                  var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
                  if (authHeader?.ToLower().StartsWith("bearer ") == true)
                  {
                      return JwtBearerDefaults.AuthenticationScheme;
                  }
                  return IdentityConstants.ApplicationScheme;
              };
          })
          .AddCookie(cfg => cfg.SlidingExpiration = true)
          .AddJwtBearer(options =>
          {
              options.TokenValidationParameters = new()
              {
                  ValidateIssuer = true,
                  ValidateAudience = true,
                  ValidateLifetime = true,
                  ValidateIssuerSigningKey = true,
      
                  ValidIssuer = jwtSettings["ValidIssuer"],
                  ValidAudience = jwtSettings["ValidAudience"],
                  IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["securityKey"])),
              };
      
          });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-30
        • 2018-01-28
        • 2017-10-03
        • 1970-01-01
        • 2019-08-31
        • 2020-10-01
        • 1970-01-01
        • 2018-02-23
        相关资源
        最近更新 更多