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

在ASP.NET Core中为Box API令牌实现缓存服务

Box API令牌缓存与自动刷新实现方案

问题背景

对接OneDrive时,MS Graph SDK内置了令牌缓存功能,通过AddInMemoryTokenCaches()即可实现令牌复用,过期时还能自动获取新令牌,无需每次请求重新获取:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
            .AddMicrosoftIdentityWebApp(options =>
            {
                builder.Configuration.Bind("AzureAd", options);
            }).EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
                    .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
                    .AddInMemoryTokenCaches();

但Box API没有这类内置功能,当前实现每次请求都会重新获取令牌(有效期60分钟),效率极低,需要实现类似的缓存与自动刷新逻辑,该方案也适用于其他无内置令牌缓存的API。

现有实现代码

服务注册(program.cs)

builder.Services.AddTransient<IBoxDrive, BoxDrive>();

接口与实现类

namespace TestProject.Repositories
{
    public interface IBoxDrive
    {
        Task<BoxFile> FileInfoAsync(string BoxId);
    }

    public class BoxDrive : IBoxDrive
    {
        private readonly IWebHostEnvironment _env;
        private readonly BoxJWTAuth _session;

        public BoxDrive(IWebHostEnvironment env) {
            var _env = env;
            var RootPath = _env.ContentRootPath;
            var boxConfigLocation = Path.Combine(System.IO.Directory.GetParent(RootPath).ToString(), "box/699422622_746zo0rh_config.json");
            var config = BoxConfigBuilder.CreateFromJsonString(System.IO.File.ReadAllText(boxConfigLocation)).Build();
            _session = new BoxJWTAuth(config);
        }

        public async Task<BoxFile> FileInfoAsync(string BoxId)
        {
            BoxFile file = null;
            try
            {
                var adminToken = await _session.AdminTokenAsync(); // 有效期60分钟,应缓存复用
                BoxClient adminClient = _session.AdminClient(adminToken);

                file = await adminClient.FilesManager.GetInformationAsync(id: BoxId);

                return file;
            }
            catch (Exception ex2)
            {
                return file;
            }
        }
    }
}

解决方案

1. 调整服务生命周期

将AddTransient改为AddSingleton,确保BoxDrive实例全局唯一,令牌缓存可跨请求复用:

builder.Services.AddSingleton<IBoxDrive, BoxDrive>();

2. 实现令牌缓存与自动刷新逻辑

修改BoxDrive类,添加缓存字段、过期检查和异步线程安全控制:

namespace TestProject.Repositories
{
    public interface IBoxDrive
    {
        Task<BoxFile> FileInfoAsync(string BoxId);
    }

    public class BoxDrive : IBoxDrive
    {
        private readonly IWebHostEnvironment _env;
        private readonly BoxJWTAuth _session;
        private string _cachedAdminToken;
        private DateTime _tokenExpiryTime;
        // 异步锁,控制并发请求下的令牌获取逻辑
        private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);

        public BoxDrive(IWebHostEnvironment env)
        {
            _env = env;
            var rootPath = _env.ContentRootPath;
            var boxConfigLocation = Path.Combine(Directory.GetParent(rootPath).ToString(), "box/699422622_746zo0rh_config.json");
            var config = BoxConfigBuilder.CreateFromJsonString(File.ReadAllText(boxConfigLocation)).Build();
            _session = new BoxJWTAuth(config);
        }

        public async Task<BoxFile> FileInfoAsync(string BoxId)
        {
            BoxFile file = null;
            try
            {
                var adminToken = await GetCachedAdminTokenAsync();
                BoxClient adminClient = _session.AdminClient(adminToken);

                file = await adminClient.FilesManager.GetInformationAsync(id: BoxId);
                return file;
            }
            catch (Exception ex)
            {
                // 建议添加日志记录异常信息,便于排查问题
                return file;
            }
        }

        private async Task<string> GetCachedAdminTokenAsync()
        {
            // 检查缓存令牌是否有效(留5分钟缓冲,避免令牌即将过期时请求失败)
            if (!string.IsNullOrEmpty(_cachedAdminToken) && DateTime.UtcNow < _tokenExpiryTime)
            {
                return _cachedAdminToken;
            }

            // 异步锁防止并发请求重复获取令牌
            await _tokenSemaphore.WaitAsync();
            try
            {
                // 双重检查,避免等待锁的线程重复获取令牌
                if (!string.IsNullOrEmpty(_cachedAdminToken) && DateTime.UtcNow < _tokenExpiryTime)
                {
                    return _cachedAdminToken;
                }

                // 获取新令牌并更新缓存
                var newToken = await _session.AdminTokenAsync();
                _cachedAdminToken = newToken;
                // Box Admin Token有效期60分钟,设置过期时间为当前UTC时间加55分钟
                _tokenExpiryTime = DateTime.UtcNow.AddMinutes(55);
                return newToken;
            }
            finally
            {
                _tokenSemaphore.Release();
            }
        }
    }
}

方案说明

  • 令牌缓存:用_cachedAdminToken存储有效令牌,_tokenExpiryTime记录过期时间,每次请求优先使用缓存令牌。
  • 过期缓冲:设置5分钟缓冲时间,避免令牌接近过期时因网络延迟等原因导致请求失败。
  • 线程安全:通过SemaphoreSlim实现异步锁,确保同一时间只有一个请求去获取新令牌,避免资源浪费和重复请求。
  • 扩展性:如果是多实例部署场景,可将内存缓存替换为分布式缓存(如Redis),实现跨实例的令牌共享。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 17:27:54