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
相关产品推荐
相关产品推荐

