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

在MVC控制器中通过Refresh Token获取过期Access Token的方法

在MVC控制器中通过Refresh Token刷新Access Token

一、后端Refresh Token接口实现(修正版)

原代码存在未定义userEmail、逻辑漏洞等问题,以下是完善后的可运行实现:

// 建议用POST请求,避免Refresh Token暴露在URL中
[Route("api/account/RefreshToken")]
[HttpPost]
public IActionResult RefreshToken([FromBody] RefreshTokenRequest request)
{
    // 从数据库查询对应Refresh Token记录
    var refreshTokenRecord = _context.UserRefreshTokens.FirstOrDefault(a => a.Token == request.RefreshToken);

    // 情况1:Refresh Token不存在,返回无效提示
    if (refreshTokenRecord == null)
    {
        return BadRequest("无效的Refresh Token");
    }

    // 情况2:Refresh Token已过期,清理过期记录并返回提示
    if (refreshTokenRecord.Expire < DateTime.Now)
    {
        _context.UserRefreshTokens.Remove(refreshTokenRecord);
        _context.SaveChanges();
        return BadRequest("Refresh Token已过期,请重新登录");
    }

    // 情况3:Refresh Token有效,关联用户并生成新Token
    var user = _context.Users.FirstOrDefault(a => a.Id == refreshTokenRecord.UserId);
    if (user == null)
    {
        return BadRequest("关联用户不存在");
    }

    // 生成新的Access Token(实际项目中不要用明文密码,用用户标识生成)
    var newAccessToken = GenerateToken(new UserForLogin
    {
        UserEmail = user.UserEmail,
        Password = user.Password
    });

    // 生成新的Refresh Token并更新数据库记录
    var newRefreshToken = GenerateRefreshToken();
    refreshTokenRecord.Token = newRefreshToken.Token;
    refreshTokenRecord.Expire = newRefreshToken.Expire;
    _context.SaveChanges();

    return Ok(new TokenResponse
    {
        AccessToken = newAccessToken,
        RefreshToken = newRefreshToken.Token
    });
}

// 请求参数模型
public class RefreshTokenRequest
{
    public string RefreshToken { get; set; }
}

// 返回结果模型
public class TokenResponse
{
    public string AccessToken { get; set; }
    public string RefreshToken { get; set; }
}

核心优化点

  • 改用POST请求提升安全性
  • 通过Refresh Token记录关联用户,避免使用未定义的userEmail
  • 过期Refresh Token自动从数据库清理
  • 生成新Refresh Token替换旧值,防止重复滥用

二、在MVC中调用Refresh Token接口

1. 全局拦截自动刷新(推荐)

自定义请求拦截器,遇到Token过期(401状态码)时自动调用刷新接口:

public class TokenRefreshHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public TokenRefreshHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        // 检测Token过期
        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            var httpContext = _httpContextAccessor.HttpContext;
            var refreshToken = httpContext.Session.GetString("RefreshToken");

            if (!string.IsNullOrEmpty(refreshToken))
            {
                // 调用刷新接口
                var refreshResponse = await new HttpClient().PostAsJsonAsync(
                    "https://你的域名/api/account/RefreshToken",
                    new RefreshTokenRequest { RefreshToken = refreshToken }
                );

                if (refreshResponse.IsSuccessStatusCode)
                {
                    var tokenResp = await refreshResponse.Content.ReadFromJsonAsync<TokenResponse>();
                    // 更新会话中的Token
                    httpContext.Session.SetString("AccessToken", tokenResp.AccessToken);
                    httpContext.Session.SetString("RefreshToken", tokenResp.RefreshToken);
                    // 重新发起原请求
                    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenResp.AccessToken);
                    response = await base.SendAsync(request, cancellationToken);
                }
            }
        }
        return response;
    }
}

2. 前端手动调用(Ajax示例)

在页面请求中捕获401错误,手动触发刷新:

function fetchData() {
    $.ajax({
        url: "/api/data",
        headers: { "Authorization": "Bearer " + localStorage.getItem("AccessToken") },
        error: function(xhr) {
            if (xhr.status === 401) {
                // 调用刷新接口
                $.post("/api/account/RefreshToken", 
                    { RefreshToken: localStorage.getItem("RefreshToken") },
                    function(tokenResp) {
                        // 更新本地存储的Token
                        localStorage.setItem("AccessToken", tokenResp.AccessToken);
                        localStorage.setItem("RefreshToken", tokenResp.RefreshToken);
                        // 重新发起请求
                        fetchData();
                    }
                ).fail(function() {
                    // Refresh Token失效,跳转登录页
                    window.location.href = "/Account/Login";
                });
            }
        }
    });
}

三、关键注意事项

  • 存储安全:Refresh Token优先存在服务器Session中,客户端存储建议用HttpOnly Cookie
  • 有效期设置:Access Token设15分钟以内,Refresh Token设7天左右
  • 回收机制:用户注销、修改密码时,立即删除对应Refresh Token记录
  • 强制HTTPS:所有Token相关请求必须用HTTPS传输,防止窃取

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 20:14:52