解决方案四(推荐★★★★★)
利用同步锁,判断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、请注意做好缓存清理工作
解决方案总结
以上解决方案没有绝对的好坏之分,要看具体的环境,因为都不会涉及到影响效率和安全性的问题,可以视情况组合使用。推荐指数更多倾向于通用性。