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

时间:2017-08-15 14:48:02

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

我正在尝试将我的身份验证内容迁移到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?}");
            });
        }
    }
}

6 个答案:

答案 0 :(得分:18)

  

导航这些变化一直很困难,但我猜测我正在做.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();
        });

        // ...
    }
}

答案 1 :(得分:17)

另一种可能性是在运行时确定选择哪种身份验证策略方案,在这种情况下,我可以使用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;
            });

            ...

            }

答案 2 :(得分:2)

https://stackoverflow.com/a/51897159/4425154的解决方案有帮助。首先要考虑的几个问题,

  1. 确保您使用的是.net core运行时2.1或更高版本
  2. 如果您使用的是中间件,请确保您遵循以下提及的授权策略

    <div class="step-1-wrapper steps ">
      <div class="step-1-inner">
        <div class="step-1-icon-wrap anim-step-1" >
            <svg class="circular-chart orange step-1-circle">
            <line stroke-dasharray="5, 5" x1="250" y1="255" x2="330" y2="255" class="step-1-2-line-mvr"></line>
            <line x1="250" y1="255" x2="330" y2="255" class="step-1-2-line-mvr-anim"></line>
            <path class="circle-bg" d="M165 170
            a 85 85 0 0 1 0 170
            a 85 85 0 0 1 0 -170"></path>
            <path class="anim-circle" d="M165 170
            a 85 85 0 0 1 0 170
            a 85 85 0 0 1 0 -170"></path>
            </svg>
        </div>
      </div>
    </div>
    
    .steps {
        display: inline-block;
        width: 24%;
        vertical-align: middle;
        text-align: center;
        margin-right: -2px;
        margin-left: -2px;
    }
    .step-1-icon-wrap {
        height: 155px;
        width: 155px;
        background-color: #ffffff;
        border-radius: 100%;
        -webkit-box-shadow: 0px 0px 10px 0px rgba(26,122,171,0.25);
        -moz-box-shadow: 0px 0px 10px 0px rgba(26,122,171,0.25);
        box-shadow: 0px 0px 10px 0px rgba(26,122,171,0.25);
        margin: 0 auto;
        margin-left: 30px;
    }
    .circular-chart {
        position: absolute;
        height: 100%;
        width: 100%;
        left: -57px;
        top: 3px;
    }
    .anim-circle {
        fill: transparent;
        stroke: #f8fcff;
        stroke-width: 3;
        stroke-dasharray: 1000;
        stroke-dashoffset: 1000;
    }
    .anim-circle {
        fill: transparent;
        stroke: #f8fcff;
        stroke-width: 3;
        stroke-dasharray: 1000;
        stroke-dashoffset: 1000;
    }
    .circle-bg {
        fill: none;
        stroke: #57c1f8;
        stroke-width: 2;
        stroke-dasharray: 6;
    }
    .step-1-2-line-mvr {
        stroke: #57c1f8;
        stroke-width: 2;
    }
    .step-1-2-line-mvr-anim {
        stroke: #f8fcff;
        stroke-width: 3;
        stroke-dasharray: 1000;
        stroke-dashoffset: 1000;
    }
    .anim-step-1 .step-1-2-line-mvr-anim {
        animation: dash 3s linear 1.5s normal 1;
    }
    .anim-step-1 .anim-circle {
        animation: dash 3s linear 1s normal 1;
    }
    
    
    .step-1-inner{
        position: relative;
        min-height: 430px;
        padding-top: 180px;
    }
    
    
    @keyframes dash {
        0% {
            stroke-dashoffset: 0;
        } 
        100% {
            stroke-dashoffset: 1000;
        }
    }
    

答案 3 :(得分:1)

//******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
{

}

答案 4 :(得分:0)

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

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 :(得分:0)

扩展@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"])),
        };

    });