解決微信公衆號OAuth出現40029(invalid code,不合法的oauth_code)的錯誤

關於OAuth

官方教程:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842&token=&lang=zh_CNhtml

原理及基本開發思路:http://www.cnblogs.com/szw/p/3764275.htmlgit

現象

在使用公衆號的OAuth過程當中,咱們有時會碰到40029(invalid code,不合法的oauth_code)的錯誤。github

緣由

其實經過官方提供的API獲取的CODE一般是不會有問題的,不可用是由於這個CODE被悄悄地用掉了。api

經過微信Web開發工具跟蹤能夠看到,微信發起了2次「相同」的請求,第一次請求被其終止掉了(也就是咱們主動發起的這一次):緩存

兩次OAuth請求

這兩次請求的Url仍是有差異的,第一次咱們經過「GetAuthorizeUrl」接口獲取到的url以下:安全

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxxx&redirect_uri=xxx&response_type=code&scope=snsapi_userinfo&state=MyState服務器

此次請求會被微信服務器中斷,而後由微信再自動發起一次:微信

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxxx&redirect_uri=xxx&response_type=code&scope=snsapi_userinfo&state=MyState&uin=MTMyMjE0NDU%3D&key=63e987ba88ddfb44972f00256f262220434daa5ef8d27994eeb1cf525b6ddf2c5fc2aeb69d08087f5c139292417a774e&pass_ticket=SISrTjV8ln27168AZM/sFXkc2yp5i2lm+rwItExPL+PxZBu2/GXq1MbUp6BvmnWAB0KtpV9nypybsh41CV2SQA=微信開發

對比能夠發現第二次請求多了兩個參數:app

&uin=MTMyMjE0NDU%3D&key=63e987ba88ddfb44972f00256f262220434daa5ef8d27994eeb1cf525b6ddf2c5fc2aeb69d08087f5c139292417a774e
&pass_ticket=SISrTjV8ln27168AZM/sFXkc2yp5i2lm+rwItExPL+PxZBu2/GXq1MbUp6BvmnWAB0KtpV9nypybsh41CV2SQA=

這兩個參數應該也是出於的安全的須要,可是這麼一來,給開發者的服務器就會帶來困擾:
第一次請求雖然微信服務器終止了,可是開發者服務器還在運行,多數狀況下已經使用了redirect_uri,並把傳遞過來的code使用掉了(code是一次性的),
當第二次請求進來的時候,咱們用相同的code天然就失效了。

解決方案一(推薦★☆☆☆☆)

從圖中能夠看到,其實兩次請求的發起者是不同的,能夠從這個角度入手,鑑別正確的請求。

固然這個方法有必定的風險:兩次請求發生的時間間隔很是小(上圖爲19毫秒),仍然須要處理異步的問題。

解決方案二(推薦★★☆☆☆)

這也是網傳的一個方案:在正常獲取了微信官方的url後面,加上&connect_redirect=1這個參數,微信就不會發起第二次,
可是本人測試沒有成功,然收到了兩次。

解決方案三(推薦★★★★☆)

既然第二次請求的參數和第一次不同,就能夠從uin和pass_ticket兩個參數進行判斷,只接受有這兩個參數的請求。

這種作法的缺點是這個請求參數並無體如今官方文檔中,或許會悄悄地進行變化,因此須要時刻關注其有效性。

此方案做爲一個條件加入到其餘方案中仍是不錯的。

解決方案四(推薦★★★★★)

利用同步鎖,判斷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繼續操做

說明:
一、上述靜態Dicitonary的儲存方式適用於單臺服務器,若是是分佈式的系統,這裏的Dictionary請使用公共緩存(如Redis),並使用分佈鎖,不然若是兩次請求命中了兩臺不一樣的服務器仍然會失效。
二、請注意作好緩存清理工做

解決方案總結

以上解決方案沒有絕對的好壞之分,要看具體的環境,由於都不會涉及到影響效率和安全性的問題,能夠視狀況組合使用。推薦指數更多傾向於通用性。

參考資料

微信開發資源:https://github.com/JeffreySu/WeixinResource/blob/master/那些年咱們踩過的坑/20160913-OAuth出現40029(invalid code)錯誤.md

微信網頁受權:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842&token=&lang=zh_CN

Senparc.Weixin.MP SDK 微信公衆平臺開發教程(十二):OAuth2.0說明:http://www.cnblogs.com/szw/p/3764275.html

相關文章
相關標籤/搜索