You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 9 Razor Pages(含Identity)Web API认证问题

Razor Page调用Web API登录后用户未认证的问题解决

问题背景

基于ASP.NET Core 9 Razor Pages + Identity搭建的项目,同时实现了供第三方客户端调用的Login Minimal API。修改Razor Page登录逻辑后,通过HttpClient调用API返回成功,但当前Page的User.Identity.IsAuthenticated仍为false,必须再次执行SignInManager.PasswordSignInAsync才能完成认证,需要找到正确的实现方式。

核心原因

HttpClient的请求上下文与浏览器的请求上下文完全独立:

  • Page中用HttpClient调用API时,是服务器内部发起的全新请求,API里的SignInManager操作的是这个内部请求的HttpContext,生成的认证Cookie只会写入内部请求的响应,不会传递给浏览器的原始请求。
  • 当前Page所在的浏览器请求上下文未被处理认证,因此User.Identity.IsAuthenticated始终为false。

解决方案

方案1:抽离共享登录逻辑(推荐)

没必要让Page绕路调用API,将登录验证的核心逻辑抽成独立服务,让Page和API复用同一逻辑,既避免重复代码,也解决上下文隔离问题。

1. 创建登录服务

public interface ILoginService
{
    Task<SignInResult> LoginAsync(string userName, string password, bool rememberMe);
}

public class LoginService : ILoginService
{
    private readonly SignInManager<MyUserType> _signInManager;

    public LoginService(SignInManager<MyUserType> signInManager)
    {
        _signInManager = signInManager;
    }

    public async Task<SignInResult> LoginAsync(string userName, string password, bool rememberMe)
    {
        return await _signInManager.PasswordSignInAsync(userName, password, rememberMe, lockoutOnFailure: false);
    }
}

2. 注册服务

在Program.cs中注入服务:

builder.Services.AddScoped<ILoginService, LoginService>();

3. 修改Razor Page的OnPostAsync

直接调用共享服务完成当前上下文的认证:

private readonly ILoginService _loginService;
private readonly IStringLocalizer<LoginModel> _localizer;

// 构造函数注入依赖
public LoginModel(ILoginService loginService, IStringLocalizer<LoginModel> localizer)
{
    _loginService = loginService;
    _localizer = localizer;
}

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl ??= Url.Content("~/");

    if (ModelState.IsValid)
    {
        var result = await _loginService.LoginAsync(Input.UserName, Input.Password, Input.RememberMe);
        if (result.Succeeded)
        {
            return LocalRedirect(returnUrl);
        }
        else
        {
            ModelState.AddModelError(string.Empty, _localizer["LoginError"].Value);
        }
    }

    return Page();
}

4. 修改Minimal API

同样调用共享服务处理第三方客户端的登录请求:

app.MapPost("/api/LoginUser", async (InputModel input, ILoginService loginService) =>
{
    var result = await loginService.LoginAsync(input.UserName, input.Password, input.RememberMe);
    if (result.Succeeded)
    {
        return Results.Ok();
    }
    return Results.BadRequest($"登录失败:{result}");
});

方案2:强制传递认证Cookie(不推荐)

如果必须让Page通过API完成登录,需要提取API响应中的认证Cookie并添加到当前Page的响应中,但这种方式冗余且易出错,仅适合特殊场景:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl ??= Url.Content("~/");

    if (ModelState.IsValid)
    {
        var cookieContainer = new CookieContainer();
        using (var handler = new HttpClientHandler { CookieContainer = cookieContainer })
        using (var client = new HttpClient(handler))
        {
            client.BaseAddress = new Uri($"{Request.Scheme}://{Request.Host}{Request.PathBase}");
            var response = await client.PostAsJsonAsync("/api/LoginUser", Input);
            
            switch (response.StatusCode)
            {
                case System.Net.HttpStatusCode.OK:
                    // 提取API返回的认证Cookie
                    var cookies = cookieContainer.GetCookies(client.BaseAddress);
                    foreach (Cookie cookie in cookies)
                    {
                        // 将Cookie添加到当前Page的响应中
                        Response.Cookies.Append(cookie.Name, cookie.Value, new CookieOptions
                        {
                            Domain = cookie.Domain,
                            Path = cookie.Path,
                            Expires = cookie.Expires,
                            Secure = cookie.Secure,
                            HttpOnly = cookie.HttpOnly,
                            SameSite = SameSiteMode.Lax
                        });
                    }
                    return Redirect(returnUrl);

                case System.Net.HttpStatusCode.BadRequest:
                    var errorText = await response.Content.ReadAsStringAsync();
                    ModelState.AddModelError(string.Empty, errorText);
                    return Page();
            }
        }
    }

    return Page();
}

总结

方案1是最佳实践,遵循DRY原则,减少重复代码同时避免上下文隔离问题;方案2仅作为特殊场景的备选,不建议常规项目使用。

内容的提问来源于stack exchange,提问作者bairog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 02:42:42