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

时间:2021-10-31 13:23:45

关于OAuth

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

原理及基本开发思路:http://www.cnblogs.com/szw/p/3764275.html

现象

在使用公众号的OAuth过程中,我们有时会碰到40029(invalid code,不合法的oauth_code)的错误。

原因

其实通过官方提供的API获取的CODE通常是不会有问题的,不可用是因为这个CODE被悄悄地用掉了。

通过微信Web开发工具跟踪可以看到,微信发起了2次“相同”的请求,第一次请求被其终止掉了(也就是我们主动发起的这一次):

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

这两次请求的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=

对比可以发现第二次请求多了两个参数:

&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继续操作

说明:

1、上述静态Dicitonary的储存方式适用于单台服务器,如果是分布式的系统,这里的Dictionary请使用公共缓存(如Redis),并使用分布锁,否则如果两次请求命中了两台不同的服务器仍然会失效。

2、请注意做好缓存清理工作

解决方案总结

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

参考资料

微信开发资源: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

解决微信公众号OAuth出现40029(invalid code,不合法的oauth_code)的错误的更多相关文章

  1. 解决微信公众号授权登录和开放平台微信第三方应用授权登录获取到的用户Openid关联问题

    开发背景: 最近一段时间一直在做关于微信方面的网站应用开发,这段时间也收获的不少关于微信开发方面的开发技能,接触的比较多的主要有微信公众号和微信网站app第三方登录授权,以及微信会员卡,优惠券和扫描二 ...

  2. 利用NATAPP隧道解决微信公众号开发之本地调试难题

    一.问题 众所周知,微信公众号开发需要公网的有效域名和80端口,本机当然互联网是访问不了的.那么我们难道去一个公网的服务器去开发吗?那样是不是太土了. 答案当然是,NO 当然我们在做微信支付的时候,有 ...

  3. ios 最新系统bug与解决——微信公众号中弹出键盘再收起时,原虚拟键盘位点击事件无效

    最近ios发布新版本系统12.1,随着部分用户的系统更新,一些问题也渐渐暴露出来... 公司用户反映微信公众号出现了点击无效的bug!!测试调查发现,只有iphonex.iphone6,ihpone7 ...

  4. 微信公众号报错 config&colon;invalid signature

    官方已经提供了微信 JS 接口签名校验工具(http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign),填入相应的参数就能出来相应的signa ...

  5. 微信公众号 JSSDK 提示:invalid signature

    要命的invalid signature.其实腾讯的文档已经写了,只能怪我自己理解能力太差,掉了好几次坑. 签名要用到的jsapi_ticket需要保存的,2小时有效期.如果在2小时内出现问题需要删除 ...

  6. Appium 解决微信公众号、小程序切换 webview 后无法定位元素的问题

    如何切换webview进入小程序请参考https://testerhome.com/topics/12003 脚本思路:进入webview后会存在多个handle同Web页签一样,获取所有的handl ...

  7. 微信公众号开发将war包导入新浪sae出现错误

    JAVA_Error: Error for /wechat.do java.lang.NoSuchFieldError: INSTANCE at org.apache.http.impl.io.Def ...

  8. thinkphp5&period;0 微信公众号接入支付宝支付

    ---恢复内容开始--- 真是无力吐槽这个需求了,想骂客户,好端端的非要在微信公众号接入支付宝,都知道微信公众号是拒绝支付宝的,屏蔽了支付宝,所以在微信公众号接入支付宝的话就必须手动复制链接跳出微信内 ...

  9. php 微信公众号接入支付宝支付

    真是无力吐槽这个需求了,好端端的非要在微信公众号接入支付宝,都知道微信公众号是拒绝支付宝的,屏蔽了支付宝,所以在微信公众号接入支付宝的话就必须手动复制链接跳出微信内置浏览器,强制性打开web浏览器完成 ...

随机推荐

  1. JavaWEB域对象

    PageContext: ServletRequest: HttpSession: ServletContext: void setAttribute(String name, Object valu ...

  2. ASP&period;NET的运行原理与运行机制 如何:为 IIS 7&period;0 配置 &lt&semi;system&period;webServer&gt&semi; 节

    https://technet.microsoft.com/zh-cn/sysinternals/bb763179.aspx 当一个HTTP请求到服务器并被IIS接收到之后,IIS首先通过客户端请求的 ...

  3. php 父类子类构造函数注意事项

    网上流传的2点: PHP的构造函数继承必须满足以下条件: 当父类有构造函数的声明时,子类也必须有声明,否则会出错. 在执行父类的构造函数时,必须在子类中引用parent关键字. 第1点不需要. 第二个 ...

  4. 看多csdn它还烂论坛?

    什么? 版权声明:本文博主原创文章,博客,未经同意不得转载.

  5. Centos下快速安装Nginx

    1.准备工作 选首先安装这几个软件:GCC,PCRE(Perl Compatible Regular Expression),zlib,OpenSSL. Nginx是C写的,需要用GCC编译:Ngin ...

  6. 小程序中点击input控件键盘弹出时placeholder文字上移

    最近做的一个小程序项目中,出现了点击input控件键盘弹出时placeholder文字上移,刚开始以为是软键盘弹出布局上移问题是传说中典型的fixed 软键盘顶起问题,因此采纳了网上搜到的" ...

  7. Belgrade Azure 2019-2-11活动感悟

    这是<国外线下技术俱乐部建设>系列文章之一.   活动网址:https://www.meetup.com/Azure-UG-Srbija/events/258673179/ 活动内容:Az ...

  8. xamarin android 文件选择

    调出选择界面: Intent intent = new Intent(Intent.ActionGetContent); intent.AddCategory(Intent.CategoryOpena ...

  9. mysql 记录的增删改查

    MySQL数据操作: DML ======================================================== 在MySQL管理软件中,可以通过SQL语句中的DML语言 ...

  10. 树莓派与Arduino Leonardo使用NRF24L01无线模块通信之基于RF24库 &lpar;二&rpar; 发送自定义数据

    在我的项目里,树莓派主要作为中心节点,用于接收数据,Arduino作为子节点,用于发送数据,考虑到以后会有很多子节点,但又不至于使得代码过于繁琐,因此所有的传输数据添加一个头部编号用于区分不同节点. ...