如何在.NET Core 6 Razor Pages中正确使用[Authorize]特性?
一、核心原因:原方案的问题
你之前的代码仅将Token添加到当前请求的响应头,但重定向到Employees页面时,新请求不会携带这个响应头的Token;同时Web项目未配置对应的身份验证方案,默认的[Authorize]基于Cookie身份验证,没有合法的身份Cookie就无法触发验证逻辑。
二、分步实现方案
1. 配置Web项目的Cookie身份验证
在.NET 6+的Program.cs(或旧版的Startup.cs)中添加Cookie身份验证服务和中间件:
var builder = WebApplication.CreateBuilder(args); // 添加Razor Pages服务 builder.Services.AddRazorPages(); // 配置Cookie身份验证 builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = "/Login"; // 未认证时自动跳转的登录页 options.ExpireTimeSpan = TimeSpan.FromHours(2); // Cookie有效期 options.AccessDeniedPath = "/AccessDenied"; // 无权限时跳转页面(可选) }); var app = builder.Build(); // 中间件顺序必须严格遵循:先认证,再授权 app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
2. 登录成功后创建Web端身份Cookie
修改Web项目登录页面的代码,在获取API Token后,同时创建Web端的身份凭证,让[Authorize]能识别已登录用户:
var userName = Request.Form["UserName"].ToString(); var password = Request.Form["Password"].ToString(); var login = new Models.Login(); login.UserName = userName; login.Password = password; var apiUrl = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build().GetSection("AppSettings")["BaseUrl"] + "/api/Authenticate/Authenticate"; using (WebClient client = new WebClient()) { client.Headers[HttpRequestHeader.ContentType] = "application/json"; string Json = JsonConvert.SerializeObject(login); var response = client.UploadString(apiUrl, Json); var result = JsonConvert.DeserializeObject<Models.LoginResponseModel>(response); if(result.code != 200) { InvalidLogin = true; return this.Page(); } else { // 1. 存储API Token到Session,供后续调用API使用 HttpContext.Session.SetString("Token", result.Message); // 2. 创建Web端身份凭证,触发Cookie验证 var claims = new List<Claim> { new Claim(ClaimTypes.Name, userName), // 可按需添加角色、用户ID等其他声明信息 }; var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var principal = new ClaimsPrincipal(identity); // 登录用户,自动生成身份Cookie await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal); return new RedirectToPageResult("/Employees"); } }
3. 完善Logout逻辑
在Web项目中添加注销逻辑,同时清除Web端身份Cookie和Session中的API Token,如需同步API端注销,需通过Token调用API的Logout接口:
public async Task<IActionResult> OnGetLogout() { // 清除Web端身份Cookie await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); // 清除Session中的API Token var token = HttpContext.Session.GetString("Token"); HttpContext.Session.Remove("Token"); // 调用API的Logout接口(需API端根据Token处理注销,而非Session) if (!string.IsNullOrEmpty(token)) { var apiUrl = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build().GetSection("AppSettings")["BaseUrl"] + "/api/Authenticate/Logout"; using (WebClient client = new WebClient()) { client.Headers.Add("Authorization", $"Bearer {token}"); client.DownloadString(apiUrl); } } return RedirectToPage("/Login"); }
4. 优化API调用的Token携带
为避免每次调用API都手动添加Token,可封装一个自动携带Token的WebClient:
public class ApiWebClient : WebClient { private readonly IHttpContextAccessor _httpContextAccessor; public ApiWebClient(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; Headers[HttpRequestHeader.ContentType] = "application/json"; var token = _httpContextAccessor.HttpContext.Session.GetString("Token"); if (!string.IsNullOrEmpty(token)) { Headers.Add("Authorization", $"Bearer {token}"); } } } // 在Program.cs中注册服务 builder.Services.AddHttpContextAccessor(); builder.Services.AddTransient<ApiWebClient>();
后续调用API时,直接注入ApiWebClient即可自动携带Token。
三、API端Logout的修正
由于Web和API是独立项目,Session不共享,API端的Logout不应依赖Session,需改为通过请求头的Token识别用户,将Token加入注销黑名单(如用Redis存储),后续API收到该Token时拒绝请求。
示例API Logout接口:
[Route("[action]")] [HttpGet] public IActionResult Logout() { // 从请求头获取Token if (Request.Headers.TryGetValue("Authorization", out var authHeader) && authHeader.ToString().StartsWith("Bearer ")) { var token = authHeader.ToString().Substring(7); // 将Token加入黑名单(示例:用Redis存储过期时间) // _redis.SetString(token, "invalid", TimeSpan.FromHours(2)); } return Ok(); }
内容的提问来源于stack exchange,提问作者user123456
相关产品推荐
相关产品推荐

