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

时间:2017-10-25 17:23:47

标签: 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?}");
        });
    }

自定义中间件:

    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>();
    }
}

1 个答案:

答案 0 :(得分:5)

简短回答:您应该使用自定义AuthorizationHandler进行身份验证&amp;检索索赔。

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

要创建自定义身份验证处理程序,您需要创建一个继承自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...");
    }
}

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

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

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

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

services.AddAuthentication()
   .AddCookie("MyAuthenticationCookie");
   .AddScheme<MyOptions, MyHandler>("MyHandlerName");

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

 app.UseAuthentication();

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

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

OR

[Authorize(AuthenticationSchemes = "MyHandlerName")]
public IActionResult MyMethod()
{  }

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

[Authorize(AuthenticationSchemes = "CookieScheme")]
public class SecuredController : Controller
{
     (...)
}

然后您需要添加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);
    }
 }

在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");

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

的Seb