Azure Web App静态变量处理及SharePoint令牌持久化问题
问题分析
你遇到的核心问题是:Azure Web App环境下,静态变量无法稳定持久化令牌。原因包括:
- Web App可能运行多实例,每个实例有独立的静态变量副本,请求分发到不同实例时会重复生成令牌
- 应用会因内存回收、部署更新、配置变更等原因重启进程,静态变量会被清空
- 即使开启Always On,也无法避免进程重启或多实例场景的影响
解决方案
下面是几种可靠的令牌存储方案,按实用性排序:
1. 内存缓存(IMemoryCache)
这是替代静态变量最简单的方案,支持设置过期时间,逻辑可控性更强,单实例场景下足够稳定。
改造后的代码示例:
public class MySharePointService : IMySharepointService { private readonly IMyLogService _myLogService; private readonly IMemoryCache _cache; private const string TokenCacheKey = "SharePointAccessToken"; // 构造函数注入IMemoryCache public MySharePointService(IMyLogService myLogService, IMemoryCache cache) { _myLogService = myLogService; _cache = cache; } private async Task GenerateAccessTokenIfNeeded() { // 先检查缓存里的令牌是否有效 if (_cache.TryGetValue(TokenCacheKey, out string existingToken) && !string.IsNullOrEmpty(existingToken)) { try { using (var context = new AuthenticationManager().GetAccessTokenContext(_siteUrl, existingToken)) { context.ExecuteQuery(); return; // 令牌能用,直接返回 } } catch (Exception) { _myLogService.Log("SharepointService", "GenerateAccessTokenIfNeeded", "Information", "缓存令牌失效,将生成新令牌"); _cache.Remove(TokenCacheKey); // 删掉失效令牌 } } // 生成新令牌 try { if (string.IsNullOrEmpty(_siteUrl) || string.IsNullOrEmpty(_clientId) || string.IsNullOrEmpty(_clientSecret)) { _myLogService.Log("SharepointService", "GenerateAccessTokenIfNeeded", "Error", "生成新令牌失败,环境变量为空"); return; } using (var context = new AuthenticationManager().GetACSAppOnlyContext(_siteUrl, _clientId, _clientSecret)) { var newToken = context.GetAccessToken(); // 设置缓存过期时间为22小时(比最大有效期24小时短,留缓冲时间) var cacheOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromHours(22)); _cache.Set(TokenCacheKey, newToken, cacheOptions); _myLogService.Log("SharepointService", "GenerateAccessTokenIfNeeded", "Information", $"新令牌已生成并缓存"); } } catch (Exception e) { _myLogService.Log("SharepointService", "GenerateAccessTokenIfNeeded", "Error", $"生成新令牌失败: {e.Message}"); } } }
注意:如果Web App是多实例部署,每个实例会有独立的内存缓存,还是可能重复生成令牌,但比静态变量稳定得多。需要跨实例共享的话,用下面的分布式缓存方案。
2. 分布式缓存(IDistributedCache + Azure Redis)
如果你的Web App是多实例部署,推荐用Azure Redis Cache做分布式缓存,确保所有实例共享同一个令牌缓存,彻底解决重复生成问题。
步骤:
- 在Azure门户创建Redis缓存实例
- 项目中安装
Microsoft.Extensions.Caching.StackExchangeRedisNuGet包 - 在Web App的应用设置里配置Redis连接字符串
- 注入IDistributedCache并替换内存缓存逻辑
核心代码示例:
public class MySharePointService : IMySharepointService { private readonly IMyLogService _myLogService; private readonly IDistributedCache _distributedCache; private const string TokenCacheKey = "SharePointAccessToken"; public MySharePointService(IMyLogService myLogService, IDistributedCache distributedCache) { _myLogService = myLogService; _distributedCache = distributedCache; } private async Task GenerateAccessTokenIfNeeded() { string existingToken = await _distributedCache.GetStringAsync(TokenCacheKey); if (!string.IsNullOrEmpty(existingToken)) { try { using (var context = new AuthenticationManager().GetAccessTokenContext(_siteUrl, existingToken)) { context.ExecuteQuery(); return; } } catch (Exception) { _myLogService.Log("SharepointService", "GenerateAccessTokenIfNeeded", "Information", "分布式缓存令牌失效,将生成新令牌"); await _distributedCache.RemoveAsync(TokenCacheKey); } } // 生成新令牌 try { if (string.IsNullOrEmpty(_siteUrl) || string.IsNullOrEmpty(_clientId) || string.IsNullOrEmpty(_clientSecret)) { _myLogService.Log("SharepointService", "GenerateAccessTokenIfNeeded", "Error", "生成新令牌失败,环境变量为空"); return; } var newToken = await GenerateNewToken(); // 设置缓存过期时间 var cacheOptions = new DistributedCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromHours(22)); await _distributedCache.SetStringAsync(TokenCacheKey, newToken, cacheOptions); _myLogService.Log("SharepointService", "GenerateAccessTokenIfNeeded", "Information", $"新令牌已生成并存入分布式缓存"); } catch (Exception e) { _myLogService.Log("SharepointService", "GenerateAccessTokenIfNeeded", "Error", $"生成新令牌失败: {e.Message}"); } } // 抽出生成令牌的独立方法,提高代码可读性 private async Task<string> GenerateNewToken() { using (var context = new AuthenticationManager().GetACSAppOnlyContext(_siteUrl, _clientId, _clientSecret)) { return context.GetAccessToken(); } } }
3. 应用设置存储(不推荐)
可以把令牌存在Web App的应用设置里,但缺点明显:
- 更新应用设置会触发应用重启
- 令牌有效期内需要手动或通过代码更新,灵活性差
- 不适合频繁更新的场景,仅作为备选方案
额外优化建议
- 抽出生成令牌的逻辑为独立方法,提升代码可维护性
- 主动跟踪令牌过期时间,在令牌过期前1小时左右主动刷新,避免请求时才发现令牌失效
- 处理并发请求:当多个请求同时检测到令牌失效时,用锁(单实例)或分布式锁(Redis)避免重复生成令牌
内容的提问来源于stack exchange,提问作者developer1926
相关产品推荐
相关产品推荐

