.NET6 MVC客户端获取Web API JWT令牌后实现用户登录咨询
实现方案
你的架构是MVC客户端作为前端交互层、Web API作为后端服务层,这种场景下不需要直接把裸JWT暴露给浏览器存储,标准做法是客户端拿到JWT后先做合法性校验,校验通过后调用Cookie认证的SignInAsync方法完成本地登录,把JWT存在加密的认证Cookie对应的身份声明中,后续调用API时再从身份信息里取出JWT携带在请求头中。
第一步:登录成功后写入本地认证Cookie
你现在已经能拿到API返回的token,不要直接把JWT明文存到普通Cookie或localStorage中,按以下流程处理:
- 强烈建议先在客户端本地校验JWT的签名、过期时间、签发方/受众,校验规则和Web API端的
TokenValidationParameters保持一致,过滤伪造、过期的无效token。 - 构造用户身份声明集合,把JWT作为单独的声明存入,再调用
HttpContext.SignInAsync写入加密的HttpOnly认证Cookie,完成本地登录。
对应登录接口的POST动作实现代码参考:
// 注意:你的登录接口必须接收用户名密码参数,且必须用POST方法,不要用GET传敏感信息 [HttpPost] public async Task<IActionResult> Login(LoginModel model, [FromServices] IConfiguration config) { using (var client = new HttpClient()) { client.BaseAddress = new Uri("https://localhost:7157/"); // 把用户名密码放到请求体传给API校验 var reqContent = new FormUrlEncodedContent(new Dictionary<string, string> { ["username"] = model.Username, ["password"] = model.Password }); HttpResponseMessage response = await client.PostAsync("WeatherForecast/login", reqContent); if (response.IsSuccessStatusCode) { var tokenResult = await response.Content.ReadFromJsonAsync<TokenModel>(); string jwtToken = tokenResult.Token; // 本地校验JWT合法性,参数和API端保持一致 var tokenHandler = new JwtSecurityTokenHandler(); var validationParams = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidAudience = config["Jwt:Audience"], ValidIssuer = config["Jwt:Issuer"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:Key"])), ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; try { // 校验不通过会直接抛出异常 var claimsPrincipal = tokenHandler.ValidateToken(jwtToken, validationParams, out _); // 追加专门存JWT的声明,后续调用API时直接取用 var userIdentity = (ClaimsIdentity)claimsPrincipal.Identity; userIdentity.AddClaim(new Claim("AccessToken", jwtToken)); // 写入本地认证Cookie,完成登录 await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(userIdentity), new AuthenticationProperties { IsPersistent = model.RememberMe, ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(20) // 和Cookie配置的过期时间对齐 }); return RedirectToAction("Index", "Home"); } catch { ModelState.AddModelError("", "登录凭证无效,请重试"); return View(model); } } ModelState.AddModelError("", "用户名或密码错误"); return View(model); } }
第二步:后续调用Web API时自动携带JWT
JWT不需要暴露给前端浏览器,MVC端后续需要调用API时,直接从当前登录用户的声明里取出之前存储的AccessToken,放到请求的Authorization头中即可:
public async Task<IActionResult> GetWeatherList([FromServices] IConfiguration config) { // 从当前用户身份中取JWT string accessToken = User.FindFirstValue("AccessToken"); using (var client = new HttpClient()) { client.BaseAddress = new Uri("https://localhost:7157/"); // 携带JWT调用API client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var data = await client.GetFromJsonAsync<IEnumerable<WeatherForecast>>("WeatherForecast"); return View(data); } }
必须注意的实践要点
- 你当前Web API端的登录接口标记为
[HttpGet]属于严重安全隐患,GET请求的参数会留在浏览器历史、服务日志中,必须改成POST方法,校验完用户名密码后再颁发JWT。 - 不要把JWT存在localStorage或者非HttpOnly的Cookie中,容易被XSS攻击窃取。通过
SignInAsync生成的认证Cookie默认是加密、HttpOnly的,安全性高很多。 - MVC客户端不需要同时启用JWT认证,只保留Cookie认证作为本地登录态凭证即可,JWT仅作为MVC服务和Web API之间的通信凭证,不需要暴露给前端浏览器。
- 如果JWT过期,可以在MVC端做全局请求拦截,要么跳回登录页,要么配合refresh token自动刷新后,更新用户声明里的AccessToken并重新调用
SignInAsync写入Cookie即可。 - 如果你完全信任内网部署的Web API返回结果,也可以跳过本地JWT校验步骤,直接解析JWT里的用户ID、角色等信息构造身份声明完成登录,但公网部署场景必须加本地校验。
内容的提问来源于stack exchange,提问作者Muhammad Ali
相关产品推荐
相关产品推荐

