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

使用YARP时,代理请求返回401如何刷新Access Token并重试请求

在YARP中处理401并自动刷新Token并重试请求

YARP官方没有直接封装这类场景的现成方案,但可以通过自定义响应处理逻辑结合请求重试来实现需求。核心思路是拦截代理返回的401响应,触发Token刷新流程,更新凭证后重新发起请求。

实现步骤与代码示例

修改你的YARP服务配置,添加响应处理逻辑,同时保留原有请求带Token的逻辑:

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetRequiredSection("ReverseProxy"))
    .AddTransforms(builder =>
    {
        // 原有逻辑:为代理请求添加Bearer Token
        builder.AddRequestTransform(async (ctx) =>
        {
            var accessToken = await ctx.HttpContext.GetTokenAsync("access_token");
            if (!string.IsNullOrEmpty(accessToken))
            {
                ctx.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            }
        });

        // 新增响应处理:拦截401并尝试刷新Token重试
        builder.AddResponseTransform(async (ctx) =>
        {
            // 仅在首次收到401且未重试时执行逻辑
            if (ctx.ProxyResponse.StatusCode == HttpStatusCode.Unauthorized 
                && !ctx.HttpContext.Items.ContainsKey("ProxyRequestRetried"))
            {
                ctx.HttpContext.Items["ProxyRequestRetried"] = true;
                var refreshToken = await ctx.HttpContext.GetTokenAsync("refresh_token");

                // 无Refresh Token则直接返回401
                if (string.IsNullOrEmpty(refreshToken)) return;

                // 调用认证服务刷新Token
                var newTokens = await FetchNewTokens(refreshToken);
                if (newTokens == null || string.IsNullOrEmpty(newTokens.AccessToken)) return;

                // 更新认证Cookie中的Token(适配Cookie存储场景)
                var authProps = new AuthenticationProperties();
                authProps.StoreTokens(new[]
                {
                    new AuthenticationToken { Name = "access_token", Value = newTokens.AccessToken },
                    new AuthenticationToken { Name = "refresh_token", Value = newTokens.RefreshToken }
                });
                await ctx.HttpContext.SignInAsync(ctx.HttpContext.User.Identity.Name, ctx.HttpContext.User, authProps);

                // 克隆原请求并替换为新Token
                var retryRequest = ctx.ProxyRequest.Clone();
                retryRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", newTokens.AccessToken);

                // 发起重试请求并替换原响应
                var forwarder = ctx.HttpContext.RequestServices.GetRequiredService<IHttpForwarder>();
                var retryResult = await forwarder.SendAsync(
                    ctx.HttpContext, 
                    ctx.DestinationPrefix, 
                    retryRequest, 
                    ctx.HttpContext.RequestAborted,
                    new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(10) });

                ctx.ProxyResponse = retryResult.Response;
            }
        });
    });

// 刷新Token的具体实现,需替换为你的认证服务接口逻辑
private async Task<TokenPair> FetchNewTokens(string refreshToken)
{
    using var httpClient = new HttpClient();
    var tokenRequest = new FormUrlEncodedContent(new Dictionary<string, string>
    {
        { "grant_type", "refresh_token" },
        { "refresh_token", refreshToken },
        { "client_id", "你的客户端ID" },
        { "client_secret", "你的客户端密钥" }
    });

    var response = await httpClient.PostAsync("https://你的认证服务器地址/token", tokenRequest);
    return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<TokenPair>() : null;
}

// 定义Token返回模型,根据认证服务返回结构调整
public class TokenPair
{
    [JsonPropertyName("access_token")]
    public string AccessToken { get; set; }

    [JsonPropertyName("refresh_token")]
    public string RefreshToken { get; set; }
}

关键注意事项

  • 避免无限重试:通过HttpContext.Items标记已重试,防止刷新Token失败后循环触发401处理逻辑。
  • Token存储适配:如果你的Token不是存在Cookie中,需要修改获取和更新Token的逻辑(比如从内存缓存、Session等获取)。
  • 认证服务适配:FetchNewTokens方法需要完全匹配你的认证服务的请求参数、请求方式和返回格式。
  • 超时与异常处理:可以根据实际业务需求添加重试超时设置、异常捕获逻辑,避免阻塞请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 17:45:13