登录后Bearer Token的安全存储及HTTP请求携带方法
问题解答
一、安全存储Bearer Token的方案及实现示例
1. HttpOnly Cookie存储(Web应用优先推荐)
HttpOnly Cookie能有效抵御XSS攻击,是Web场景下最安全的Token存储方式之一。修改你的登录方法,获取Token后存入Cookie:
[HttpPost] public async Task<ActionResult<string>> Login([FromForm] LoginViewModel model) { var httpClient = _httpClientFactory.CreateClient("HousingWebAPI"); var body = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json"); using (var posting = await httpClient.PostAsync("Login/login", body)) { posting.EnsureSuccessStatusCode(); var content = await posting.Content.ReadAsStringAsync(); // 反序列化API返回的认证结果 var authResult = JsonConvert.DeserializeObject<AuthResult>(content); // 配置Cookie参数 var cookieOptions = new CookieOptions { HttpOnly = true, // 禁止前端JS读取,防范XSS Secure = true, // 仅HTTPS传输,生产环境必须启用 SameSite = SameSiteMode.Strict, // 防范CSRF攻击 Expires = DateTimeOffset.UtcNow.AddHours(1) // 与API返回的Token有效期保持一致 }; Response.Cookies.Append("AuthToken", authResult.Token, cookieOptions); return Ok(content); } return NotFound(); } // 用于反序列化API返回结果的模型类 public class AuthResult { public string Token { get; set; } // 可按需添加过期时间、用户名等其他字段 }
2. 数据库存储(多设备持久化场景)
如果需要支持用户多设备登录,可将Token关联用户ID存入数据库,同时用Cookie存储Refresh Token用于续期:
// 登录时的存储逻辑示例 var authResult = JsonConvert.DeserializeObject<AuthResult>(content); var userToken = new UserToken { UserId = 当前用户ID, // 从登录结果或会话中获取用户ID AccessToken = authResult.Token, ExpiresAt = DateTime.UtcNow.AddHours(1), RefreshToken = Guid.NewGuid().ToString() }; // 写入数据库 await _userTokenRepository.AddAsync(userToken); // 将Refresh Token存入HttpOnly Cookie var refreshCookieOptions = new CookieOptions { HttpOnly = true, Secure = true, SameSite = SameSiteMode.Strict, Expires = DateTimeOffset.UtcNow.AddDays(7) }; Response.Cookies.Append("RefreshToken", userToken.RefreshToken, refreshCookieOptions);
二、在PostData方法中添加Authorization头
从存储位置(以Cookie为例)读取Token,添加到HttpClient的请求头即可:
public async Task<ActionResult<string>> PostData([FromForm] DataTableAjaxPostModel model) { // 从Cookie中获取Token var token = Request.Cookies["AuthToken"]; if (string.IsNullOrEmpty(token)) { return Unauthorized("未获取到认证Token"); } using var client = new HttpClient(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // 添加Bearer Token到Authorization头 client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); var body = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json"); var response = await client.PostAsync("https://localhost:7193/HousingWebAPI/GetData", body); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); return Ok(content); } return NotFound(); }
优化建议:使用HttpClientFactory创建客户端
避免每次请求创建新HttpClient,复用IHttpClientFactory可以防止Socket资源耗尽问题:
private readonly IHttpClientFactory _httpClientFactory; // 构造函数注入HttpClientFactory public YourController(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task<ActionResult<string>> PostData([FromForm] DataTableAjaxPostModel model) { var token = Request.Cookies["AuthToken"]; if (string.IsNullOrEmpty(token)) { return Unauthorized("未获取到认证Token"); } var client = _httpClientFactory.CreateClient("HousingWebAPI"); // 使用预配置的命名客户端 client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); var body = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json"); // 命名客户端已配置BaseAddress,无需写完整URL var response = await client.PostAsync("HousingWebAPI/GetData", body); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); return Ok(content); } return NotFound(); }
内容的提问来源于stack exchange,提问作者Jose Daniel Navarro Brito
相关产品推荐
相关产品推荐

