解决方案四(推荐★★★★★)
利用同步锁,判断code的使用情况,这是最粗犷也是最彻底的方法,以 C# 使用 Senparc.Weixin SDK 为例,直接上代码:
定义静态变量:
static Dictionary<string, OAuthAccessTokenResult> OAuthCodeCollection = new Dictionary<string, OAuthAccessTokenResult>();
static object OAuthCodeCollectionLock = new object();
回调方法内:
string openId;
OAuthAccessTokenResult result = null;
try
{
//通过,用code换取access_token
var isSecondRequest = false;
lock (OAuthCodeCollectionLock)
{
isSecondRequest = OAuthCodeCollection.ContainsKey(code);
}
if (!isSecondRequest)
{
//第一次请求
LogUtility.Weixin.DebugFormat("第一次微信OAuth到达,code:{0}", code);
lock (OAuthCodeCollectionLock)
{
OAuthCodeCollection[code] = null;
}
}
else
{
//第二次请求
LogUtility.Weixin.DebugFormat("第二次微信OAuth到达,code:{0}", code);
lock (OAuthCodeCollectionLock)
{
result = OAuthCodeCollection[code];
}
}
try
{
try
{
result = result ?? OAuthApi.GetAccessToken(SiteConfig.YourAppId, SiteConfig.YourAppSecret, code);
}
catch (Exception ex)
{
return Content("OAuth AccessToken错误:" + ex.Message);
}
if (result != null)
{
lock (OAuthCodeCollectionLock)
{
OAuthCodeCollection[code] = result;
}
}
}
catch (ErrorJsonResultException ex)
{
if (ex.JsonResult.errcode == ReturnCode.不合法的oauth_code)
{
//code已经被使用过
lock (OAuthCodeCollectionLock)
{
result = OAuthCodeCollection[code];
}
}
}
openId = result != null ? result.openid : null;
}
catch (Exception ex)
{
return Content("授权过程发生错误:" + ex.Message);
}
//使用result继续操作
说明:
1、上述静态Dicitonary的储存方式适用于单台服务器,如果是分布式的系统,这里的Dictionary请使用公共缓存(如Redis),并使用分布锁,否则如果两次请求命中了两台不同的服务器仍然会失效。
2、请注意做好缓存清理工作
解决方案总结
以上解决方案没有绝对的好坏之分,要看具体的环境,因为都不会涉及到影响效率和安全性的问题,可以视情况组合使用。推荐指数更多倾向于通用性。