使用cookie身份验证的ASP.NET Core 2.0自定义中间件

use*_*457 4 authentication cookies asp.net-core asp.net-core-middleware

我需要为我的公司实施自定义"身份验证".我在引号中说这是因为用户在技术上在它到达应用程序之前进行了身份验证,如果是这样,userId将存在于请求标头中.

我需要做的是找出一种方法来查询数据库并根据该Id获取其他用户信息,并设置HttpContext.User对象,以便在应用程序中轻松使用它.

我现在采取的路线涉及使用没有ASP.NET核心身份的Cookie身份验证.我已将该想法与将为用户查询数据库的自定义中间件相结合,从db字段填充声明,并使用context.SignInAsync创建cookie.我把这个中间件放在app.UseAuthentication()之前.问题是在第一次请求时没有设置.User对象,因为看起来SignIn方法只创建cookie但不设置.User对象.身份验证中间件尚未看到cookie,因为它在第一次请求时不存在.

谁能提供任何想法?也许我错了,或者这种技术很好,但是我错过了我需要做的工作.

在Startup.cs中:

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();


        services.AddAuthentication("MyAuthenticationCookie")
           .AddCookie("MyAuthenticationCookie");
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseMyUserMiddleware();

        app.UseAuthentication();

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

自定义中间件:

    public class MyUserMiddleware
{
    private readonly RequestDelegate _next;

    public MyUserMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public Task Invoke(HttpContext context)
    {
        // Sign in user if this auth cookie doesn't exist
        if (context.Request.Cookies[".AspNetCore.MyAuthenticationCookie"] == null)
        {
            // Get user from db - not done

            // Set claims from user object - put in dummy test name for now
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, "TEST"),

            };

            var claimsIdentity = new ClaimsIdentity(claims, "MyAuthenticationCookie");

            context.SignInAsync("MyAuthenticationCookie", new ClaimsPrincipal(claimsIdentity));
        }

        return this._next(context);
    }
}

public static class MyUserMiddlewareExtensions
{
    public static IApplicationBuilder UseMyUserMiddleware(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyUserMiddleware>();
    }
}
Run Code Online (Sandbox Code Playgroud)

Seb*_*Seb 6

简短回答:您应该使用自定义AuthorizationHandler来验证和检索声明.

答案很长:使用ASP.NET CORE,您应该远离身份验证中间件.相反,你应该使用AuthenticationHandler 微软

要创建自定义身份验证处理程序,您需要创建一个继承自的新类AuthenticationHandler<TOption>.TOption是一个用于将参数传递给处理程序的简单类.

public class TecMobileOptions : AuthenticationSchemeOptions
{ 
   // Add your options here
}

public class MyNewHandler : AuthenticationHandler<MyOptions>
{
    private readonly ILogger _logger;

    public TecMobileHandler(
        IOptionsMonitor<MyOptions> options,
        ILoggerFactory loggerFactory,
        UrlEncoder encoder,
        ISystemClock clock) : base(options, loggerFactory, encoder, clock)
    {
       // Inject here your DbContext
        _logger = loggerFactory.CreateLogger("name...");
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您需要实现HandleAuthenticateAsync方法.必要时,Auth中间件将调用它:

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var authorization = Request.Headers["UserId"].ToString();
        (...)
        return AuthenticateResult.Success(
            new AuthenticationTicket(**your claims**, Scheme.Name));
    }
Run Code Online (Sandbox Code Playgroud)

此方法返回的声明将通过HttpContext.User对象提供.

完成后,您需要将您的方案添加到身份验证构建器.

services.AddAuthentication()
   .AddCookie("MyAuthenticationCookie");
   .AddScheme<MyOptions, MyHandler>("MyHandlerName");
Run Code Online (Sandbox Code Playgroud)

不要忘记在Startup.cs/Configure方法中添加以下代码行

 app.UseAuthentication();
Run Code Online (Sandbox Code Playgroud)

最后,您需要在要保护的所有类/方法上添加Authorize属性

[Authorize(AuthenticationSchemes = "MyHandlerName")]
public class MyControllerController : BaseController
{  }

OR

[Authorize(AuthenticationSchemes = "MyHandlerName")]
public IActionResult MyMethod()
{  }
Run Code Online (Sandbox Code Playgroud)

编辑:这里的解决方案涵盖了完整的登录过程.让我们考虑你定义两个身份验证方案 - 基于Cookie的名为CookieScheme - AutoSignInScheme:按照上述步骤创建相应的处理程序

[Authorize(AuthenticationSchemes = "CookieScheme")]
public class SecuredController : Controller
{
     (...)
}
Run Code Online (Sandbox Code Playgroud)

然后你需要添加 AccountController

public class AccountController : Controller
{
    [HttpGet]
    [Authorize(AuthenticationSchemes = "AutoSignInScheme")]
    public async Task<IActionResult> AutoSignIn(string returnUrl)
    {
        await HttpContext.SignInAsync(
           "CookieScheme",
           new ClaimsPrincipal(new ClaimsIdentity(User.Claims, "CookieScheme")));
        return Redirect(returnUrl);
    }
 }
Run Code Online (Sandbox Code Playgroud)

在Startup.cs中,添加以下行:

       services.AddAuthentication()
            .AddCookie("CookieScheme", opts =>
            {
                opts.LoginPath = new PathString("/account/AutoSignIn");
                opts.LogoutPath = ** TODO IF REQUIRED **
                opts.Cookie.Expiration = TimeSpan.FromHours(8);
            })
            .AddScheme<MyOptions, MyHandler>("AutoSignInScheme");
Run Code Online (Sandbox Code Playgroud)

当用户尝试访问您的站点时,他将被重定向到autosignin控制器.然后从您的数据库中检索声明,存储在cookie中,用户最终会重定向到他的初始目的地!

勒布