• 再谈微信公众号网页授权的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 第一步,拿到code
        oauth_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:
            pass
        else:
            #一致,重新生成随机数令牌,防止第二次访问造成code失效
            RADOM_STATE = str(random.randint(1000, 9999))
            # print(RADOM_STATE)
    
            # 2 第二步, 换取access_token
            url = '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_STATE
    
        REDIRECT_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.cn</title>
    </head>
    <body>
    
        <h1>Hello</h1>
        <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>
  • 相关阅读:
    ubuntu查看软件安装位置
    es search
    es
    Elasticsearch 之python
    用户登陆注册,修改密码
    Django基础—— 9.ORM机制讲解
    Django基础—— 8.数据库配置
    Django基础—— 7.View函数(2)
    Django基础—— 7.View函数(1)
    Django基础—— 6、URL分发器
  • 原文地址:https://www.cnblogs.com/skysowe/p/9330423.html
Copyright © 2020-2023  润新知