为什么身份验证 Cookie 对 [Authorize] 属性不起作用?

Sim*_*mon 2 c# asp.net-core asp.net-core-webapi blazor blazor-webassembly

我们尝试在 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
            });

        // [...]
    }
}
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

现在下面Controller有一个 Search-Action 并且应该通过添加 [Authorize] 属性来限制访问:

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

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

为什么我的身份验证 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");
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

pok*_*oke 6

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

app.UseRouting();

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

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});
Run Code Online (Sandbox Code Playgroud)

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

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

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

在您的原始代码中,如果没有运行身份验证,这也解释了您被重定向的原因:由于身份验证方案未运行以对用户进行身份验证,因此没有登录用户(即使用户有 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
    });
Run Code Online (Sandbox Code Playgroud)

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

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

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

    return LocalRedirect(returnUrl);
}
Run Code Online (Sandbox Code Playgroud)

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