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

ASP.NET Core 6 Razor Pages需配置仅认证用户访问,该用哪种认证类型?

解决方案

1. 确定适用的认证类型

你需要同时配置两种认证方案,分别适配页面和API的需求:

  • Cookie认证:用于保护Razor Pages(服务器端渲染页面的身份验证)
  • JWT Bearer认证:用于后续API调用的Token验证(匹配外部返回JWT的认证逻辑)

两种方案可共存,分别处理页面访问控制和API请求的Token校验。

2. 核心配置与实现步骤

2.1 配置服务(Program.cs)

在AddAuthentication中同时注册Cookie和JWT Bearer方案,并指定默认的认证、挑战规则:

builder.Services.AddAuthentication(options =>
{
    // 默认用Cookie认证处理页面访问
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
    // 未认证用户跳转的登录路径
    options.LoginPath = "/Login";
    // 权限不足时的跳转路径
    options.AccessDeniedPath = "/AccessDenied";
    // 可自定义Cookie过期时间、安全属性等
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        // 替换为你的实际Issuer、Audience和签名密钥
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
    };
});

// 配置全局授权策略:默认要求用户已认证
builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

2.2 启用认证授权中间件

确保中间件顺序正确,认证必须在授权之前:

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

2.3 豁免登录页和首页的认证

在对应页面的PageModel上添加[AllowAnonymous]特性,跳过全局认证要求:

  • 首页(Index.cshtml.cs):
[AllowAnonymous]
public class IndexModel : PageModel
{
    // 页面逻辑
}
  • 登录页(Login.cshtml.cs):
[AllowAnonymous]
public class LoginModel : PageModel
{
    // 登录逻辑
}

2.4 登录获取JWT并完成Cookie认证

调用外部登录接口拿到JWT后,需要同时完成Cookie认证(让Razor Pages识别用户已登录)和Token存储:

public async Task<IActionResult> OnPostAsync(LoginInputModel model)
{
    // 调用外部登录接口获取JWT
    var tokenResponse = await _httpClient.PostAsJsonAsync("/api/auth/login", model);
    if (!tokenResponse.IsSuccessStatusCode)
    {
        ModelState.AddModelError("", "登录失败");
        return Page();
    }

    var jwtToken = await tokenResponse.Content.ReadFromJsonAsync<JwtTokenResponse>();

    // 创建Cookie认证的身份凭证
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.Name, model.Username),
        // 将JWT存入Claim,方便后续API调用取出
        new Claim("JwtToken", jwtToken.AccessToken)
    };

    var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
    var authProperties = new AuthenticationProperties
    {
        IsPersistent = model.RememberMe,
        ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30)
    };

    // 完成Cookie登录
    await HttpContext.SignInAsync(
        CookieAuthenticationDefaults.AuthenticationScheme,
        new ClaimsPrincipal(claimsIdentity),
        authProperties);

    // 存储RefreshToken到Session,用于后续刷新
    HttpContext.Session.SetString("RefreshToken", jwtToken.RefreshToken);

    return RedirectToPage("/Index");
}

2.5 API调用时携带JWT Token

从用户Claim中取出JWT,添加到API请求头:

var jwtToken = HttpContext.User.FindFirstValue("JwtToken");
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);

// 发起API请求
var response = await _httpClient.GetAsync("/api/data");

2.6 Token刷新逻辑

当Token过期时,用存储的RefreshToken调用刷新接口,更新Token和Session:

public async Task<IActionResult> RefreshTokenAsync()
{
    var refreshToken = HttpContext.Session.GetString("RefreshToken");
    if (string.IsNullOrEmpty(refreshToken))
    {
        return RedirectToPage("/Login");
    }

    var refreshResponse = await _httpClient.PostAsJsonAsync("/api/auth/refresh", new { RefreshToken = refreshToken });
    if (!refreshResponse.IsSuccessStatusCode)
    {
        return RedirectToPage("/Login");
    }

    var newToken = await refreshResponse.Content.ReadFromJsonAsync<JwtTokenResponse>();
    // 更新Session中的Token
    HttpContext.Session.SetString("JwtToken", newToken.AccessToken);
    HttpContext.Session.SetString("RefreshToken", newToken.RefreshToken);

    // 更新Cookie中的JWT Claim(可选)
    var claimsIdentity = (ClaimsIdentity)User.Identity;
    var jwtClaim = claimsIdentity.FindFirst("JwtToken");
    if (jwtClaim != null)
    {
        claimsIdentity.RemoveClaim(jwtClaim);
        claimsIdentity.AddClaim(new Claim("JwtToken", newToken.AccessToken));
        await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, User);
    }

    return RedirectToPage("/Dashboard");
}

3. 解决"No authenticationScheme was specified..."异常

这个错误的核心原因是未指定默认认证/挑战方案,或中间件顺序错误:

  • 确保在AddAuthentication中明确设置DefaultAuthenticateScheme和DefaultChallengeScheme为Cookie认证方案
  • 中间件顺序必须是UseAuthentication()在前,UseAuthorization()在后
  • 检查授权策略中是否引用了不存在的认证方案

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 12:13:53