• 再谈微信公众号网页授权的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>
  • 相关阅读:
    POJ 3261 Milk Patterns (求可重叠的k次最长重复子串)
    UVaLive 5031 Graph and Queries (Treap)
    Uva 11996 Jewel Magic (Splay)
    HYSBZ
    POJ 3580 SuperMemo (Splay 区间更新、翻转、循环右移,插入,删除,查询)
    HDU 1890 Robotic Sort (Splay 区间翻转)
    【转】ACM中java的使用
    HDU 4267 A Simple Problem with Integers (树状数组)
    POJ 1195 Mobile phones (二维树状数组)
    HDU 4417 Super Mario (树状数组/线段树)
  • 原文地址:https://www.cnblogs.com/skysowe/p/9330423.html
Copyright © 2020-2023  润新知