再谈微信公众号网页授权的40163错误
{"errcode":40163,"errmsg":"code been used}
进来看这篇文章的你,肯定在微信公众号开发的时候看到过这行代码吧,没错,通过点击微信公众号菜单或者其他方式做oauth认证(比如微信支付估计也会要用到),都会要通过oauth认证拿到访问者的openid,以及user_info,这个code只能用一次,但是在安卓手机上,点击一次会产生两次访问,导致获取access_token失败!
2017年7月,我遇到了这个问题,当时在SegmentFault https://segmentfault.com/q/1010000010308461 里提了问,后来又跟踪了两天,也没彻底搞懂,反正时好时坏,,后来自己主要精力放在H5+开发上,就不怎么关心公众号这块了;
2018年7月,我又带着python重回公众号,又遇到了这个问题,这次绕不过去了,一年过去了,网上搜索一下,问这个问题的人还是很多,而且也没有真正解决这个问题,
比如这条:https://developers.weixin.qq.com/blogdetail?action=get_post_info&lang=zh_CN&token=&docid=b8f9f09573e92ffb0e23308d54bcdcf7
再比如:https://blog.csdn.net/hejisan/article/details/80374113
其实这个问题也不复杂,总结起来说,就是“点击公众号菜单产生两次请求”,导致code被用了两次,但是因为微信官方的文档没有相关内容,大家也都用自己的方式解决,我的解答应该也只是其中一种吧。
有人说:“后来发现只要加个属性就不会有这个问题了。
https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx&redirect_uri=xxx&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect
&connect_redirect=1 这个参数”
!很可惜,这条加上去,对我一点也不起作用!
又仔细阅读微信官方的文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
这里有
appid
redirect_uri
response_type
scope
state
wechat_redirect
的作用说明,但是并没有上面提到的connect_redirect,
提到了一个state的参数,这个是干嘛用的呢?
/
state 否 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
/
测试的时候发现,在访问https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx&redirect_uri=xxx&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect的时候,无论state填什么,获取code的时候,这个字段会原样返回过来,好就用它来解决。
既然这个参数会被原样返回过来,那我就利用它作为一个令牌,发送之前先生成一个随机数,然后保存到一个全局变量里,在redirect_uri页面的接收到这个随机数,跟全局变量对比一下,如果一样,就把这个令牌改掉,这样第二次访问的时候,这个令牌就失效了,就pass掉这个请求,避免了被2次访问的情况。
PS.
纯php没有全局变量,用一些框架应该就会有,或者写到session或者缓存都行;
我是在django里开发的,所以就直接搞了个全局变量,简单粗暴。
我是python初学者,代码写得比较拖沓啰嗦,见谅。
有人说是微信内置浏览器的问题,也有人说是nginx的问题,我觉得应该不是nginx,因为这次用python用的是django内置的服务器,也同样情况。这个问题在苹果手机上好像就没有,主要是在安卓版微信上有,因为自己已经不用苹果手机了,所以也没有做测试。
最后,上代码:
我的开发环境:
python 3.5.3
django 2.0.7
view.py代码如下:
import json import random import requests#全局变量 APP_ID = '公众号ID' App_SEC = '公众号SEC' RADOM_STATE = ""def oauth(request):global RADOM_STATE_code = request.GET#1 第一步,拿到codeoauth_state = _code['state']oauth_code = _code['code']oauth_info_json = {'oauth_state' : oauth_state,'oauth_code' : oauth_code}#print("=====>" + json.dumps(oauth_info_json))print(oauth_state) #本次拿到的随机数令牌print(RADOM_STATE) #全局变量里保存的跳转之前生成的随机数令牌#比较本次state和全局随机数令牌是否一致,不一致,说明是第二次访问,丢弃if RADOM_STATE != oauth_state:passelse:#一致,重新生成随机数令牌,防止第二次访问造成code失效RADOM_STATE = str(random.randint(1000, 9999))# print(RADOM_STATE)# 2 第二步, 换取access_tokenurl = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code'.format(APP_ID, App_SEC, oauth_code)print(url)oauth_access_token = requests.get(url)print("=====>" + oauth_access_token.text)re_json_str = oauth_access_token.text# print(type(re_json_str), re_json_str)re_json = json.loads(re_json_str)# print(type(re_json), re_json)# 3 第三步,拿到用户信息try:url = 'https://api.weixin.qq.com/sns/userinfo?access_token={}&openid={}&lang=zh_CN'.format(re_json['access_token'], re_json['openid'])print(url)user_info = requests.get(url)#print(user_info.encoding)#print(user_info.text.encode("iso-8859-1").decode('utf8')) user_info_json_str = user_info.text.encode("iso-8859-1").decode('utf8') #微信传过来的中文使用的iso8859编码,为了在模板里能显示出来,要转换成utf8编码# print(type(user_info_json_str), user_info_json_str) user_info_json = json.loads(user_info_json_str)# print(type(user_info_json), user_info_json)except Exception as e:print(e)content = {'oauth_info': oauth_info_json, #oauth_info_json'user_info': user_info_json #user_info_json }return render(request, 'user_info.html', content) #把数据给html模板,其实oauth_info_json没必要传了,只需要user_inf_json就够了,这里只是为了演示!def user_info(request):global RADOM_STATEREDIRECT_URI = "http://www.xxxxxxxxx.cn/weixin/oauth/" #跳转的地址SCOPE = "snsapi_userinfo"STATE = str(random.randint(1000,9999)) #生成随机数RADOM_STATE = STATE #随机数给全局变量redirect_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={}&redirect_uri={}&response_type=code&scope={}&state={}&connect_redirect=1#wechat_redirect".format(APP_ID, REDIRECT_URI, SCOPE, STATE)# print(redirect_url)return HttpResponseRedirect(redirect_url) #向微信服务器做跳转
user_info.html模板:
DOCTYPE html>
<html lang="en">
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><title>yoooko.cntitle>
head>
<body><h1>Helloh1><h3>oauth_state: h3><p>{{ oauth_info.oauth_state }}p><h3>oauth_code: h3><p>{{ oauth_info.oauth_code }}p><p><b>{{ user_info }}b>p>body>
html>
转载于:https://www.cnblogs.com/skysowe/p/9330423.html
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
