为什么 Authentication Cookie 对 [Authorize] 属性不起作用?

Why is the Authentication Cookie not working against the [Authorize] attribute?

我们尝试在我们的 Blazor-WebAssembly 应用程序中通过 Cookie 实施身份验证。

控制器: 设置 Auth-Cookie:

[Route("[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    [HttpPost]
    public async Task<AdUser> Login(Credentials pCredentials)
    {
        // [...] credential check jere

            var lClaims = new List<Claim> {
                new Claim(ClaimTypes.Name, "SamAccountName"),
            };
            var lClaimsIdentity = new ClaimsIdentity(lClaims, CookieAuthenticationDefaults.AuthenticationScheme);

            // set cookie
            await HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme,
            new ClaimsPrincipal(lClaimsIdentity),
            new AuthenticationProperties
            {
                IsPersistent = true,
                ExpiresUtc = DateTime.UtcNow.AddYears(1),
                RedirectUri = this.Request.Host.Value
            });

        // [...]
    }
}

当我查看 edge 浏览器的开发人员工具时,我发现 cookie 已设置:

现在以下 Controller 有一个搜索操作,应该通过添加 [Authorize] 属性来限制访问:

[Route("[controller]")]
[ApiController]
public class ClientsController : ControllerBase
{
    [HttpGet("search")]
    [Authorize]
    public ActionResult<List<Shared.Client>> Search(string pText)
    {
        // [...] Code here
        
        return lResult;
    }
}

当我向 ClientsController 发出 HTTP 请求 /Clients?search=My Search Text 时,Edge 的开发人员工具向我显示,有一个向 /Account/Login 发出的请求。这让我感到困惑,因为响应代码是 200 但我的项目中不存在帐户控制器。

为什么我的身份验证 Cookie 不能针对 [Authorize] 属性工作?

关于我的配置的一些进一步细节:

Startup.cs(服务器端)

namespace BlazorWebAssemblyApp.Server
{
    public class Startup
    {
        /// [...]

        public void ConfigureServices(IServiceCollection services)
        {
            // [...]
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(); // This line is required for the authentication cookie       
            // [...]
        }
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // [...]

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("index.html");
        });
    }
}

如果您在使用 cookie 身份验证方案显式登录后发现用户在以后的请求中无法被识别,则表明您没有正确配置身份验证中间件。作为 per the docs,您不仅需要使用 services.AddAuthentication(…) 添加身份验证服务,还必须将身份验证中间件配置为 运行 作为请求管道的一部分。这通常看起来像这样:

app.UseRouting();

// add the call to `UseAuthentication`
app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

通过将 UseAuthentication() 调用添加到中间件中,您将导致默认身份验证方案(在您的情况下为 cookie 方案)运行 尝试对用户进行身份验证。这确保如果请求中有身份验证 cookie,那么它将用于对用户进行身份验证,无论您是否要访问授权路由。

一旦中间件 运行s,仅受 [Authorize] 属性保护的操作也将起作用,因为 cookie 方案的身份验证已经发生(因为它是默认方案)。

否则,如果默认情况下不调用中间件,则需要确保在需要访问用户信息时始终显式调用身份验证方案。这就是 [Authorize(AuthenticationSchemes = "scheme-name")] 所做的:在 授权 运行 之前,它将尝试验证指定的方案。 – 如果您使用身份验证中间件并且具有正确的默认方案,那么您可以跳过此步骤,因为该方案将默认进行身份验证。

在您的原始代码中,没有身份验证 运行ning,这也解释了您被重定向的原因:由于身份验证方案没有 运行 对用户进行身份验证,因此没有登录用户(即使用户有 cookie)。因此,当用户 已授权 时,没有用户在那里,您将被重定向到登录页面。

为什么会重定向到 /Account/Login

cookie 身份验证方案涉及在需要身份验证(例如通过 [Authorize] 属性)但用户还没有身份验证 cookie 时将用户重定向到登录页面。在这种情况下,身份验证将受到“挑战”,对于 cookie 方案而言,这意味着用户将被重定向到他们应该登录的登录页面。

默认情况下,登录页面的路由配置为/Account/Login。当您使用 ASP.NET Core Identity 时,此默认值与默认行为相匹配。您可以通过更改 CookieAuthenticationOptions.LoginPath 轻松配置此路由以匹配您的实际登录页面。例如,您可以使用 AddCookie() 调用:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Auth/Login"; // using the AuthController instead
    });

现在,当用户受到挑战时,他们将被重定向到您的 AuthController.Login 操作,而不是他们应该登录的地方。

请注意,cookie 方案将向登录操作添加一个请求参数 ReturnUrl,其中包含用户最初尝试访问的页面的路径。例如。当访问您的搜索操作时,他们将被重定向到 /Auth/Login?ReturnUrl=%2FClients%2Fsearch。因此,您应该接受此路由参数,并在登录完成后 return 返回该路由,例如:

[HttpPost]
public async Task<IActionResult> Login(Credentials pCredentials, string returnUrl)
{
    // do login

    return LocalRedirect(returnUrl);
}

您还可以通过更改 CookieAuthenticationOptions.ReturnUrlParameter.

将参数 ReturnUrl 的名称更改为任何您喜欢的名称

您将被重定向到“登录页面”或 returnURL,因为您的身份验证工作不正常并且您正在获得未授权。 ASP.Net Core 在验证失败时默认重定向您,而不是返回 401 代码。

确保您按照 https://docs.microsoft.com/es-es/aspnet/core/security/authentication/identity?view=aspnetcore-3.1&tabs=visual-studio 中所述的正确方式实施它。

不要忘记在 Startup.cs class 中的 Configure 方法中添加以下行以添加 auth 中间件:

app.UseAuthentication();
app.UseAuthorization();

请检查您的顺序是否正确。(顺序很重要,因为您首先进行身份验证,然后它会检查您的角色)。

它们也必须放在 app.UseRouting()app.UseEndpoints() 调用之间。

我自己得到的,在控制器中使用 [Authorize(AuthenticationSchemes = AuthSchemes)] 作为我的操作。这是代码:

[Route("[controller]")]
[ApiController]
public class ClientsController : ControllerBase
{          
    private const string AuthSchemes = CookieAuthenticationDefaults.AuthenticationScheme;

    [HttpGet("search")]
    [Authorize(AuthenticationSchemes = AuthSchemes)]
    public ActionResult<List<Shared.Client>> Search(Shared.Client.SearchProperty pProperty, string pText)
    {
        // [...]
    }
}

您可以在此处阅读有关此主题的更多信息:https://docs.microsoft.com/de-de/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-3.1