解决微信公众号OAuth出现40029(invalid code,不合法的oauth_code)的错误

解决方案四(推荐★★★★★)

利用同步锁,判断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、请注意做好缓存清理工作

解决方案总结

以上解决方案没有绝对的好坏之分,要看具体的环境,因为都不会涉及到影响效率和安全性的问题,可以视情况组合使用。推荐指数更多倾向于通用性。

参考资料

原文地址:https://www.cnblogs.com/raincedar/p/10297945.html