使用Django实现微信公众号扫码登陆非OAuth2.0协议

公司最近做了个论坛使用django开发的,其中用户登陆部分打算升级为微信扫码登陆,调查了一些资料终于实现,现把实现方法贴出来大家一起学习下。微信现在接口现在越来越严格了,每出点新功能都要各种验证,而且接口调用还不固定,现在就一家独大程序员只能各种忍了。

这次开发没有采用微信推荐的OAuth2.0协议方式实现微信扫码登陆,OAuth2.0协议要求比较多,首先你必须是服务号,你的账号需要注册并通过微信开放平台认证,有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。

微信开放平台认证的认证非常麻烦,而且已经认证的微信公账号居然还要认证一次,我只想说中国程序员的工作量就是被这些大公司的不负责造成的。不使用OAuth2.0怎样实现微信扫码网页里的二维码后自动登录呢?

  • 我采取的方式是这样的,我们可以调用微信公众号的生成临时二维码接口;
  • 获取一张二维码把登录用login_code信息保存到二维码里,把这张图片显示到网页里;
  • 用户扫码后会出发一个SCAN事件,这个微信会把这个事件出发的信息发送回我们的服务器;
  • 服务器接收到SCAN事件后取出login_code和用户唯一id,就可以实现注册登录流程了;
  • 另外我们可以用用户唯一id再发送一个请求去微信服务器获取用户详细信息;
  • 这样微信扫码登陆流程就完整了,而且可以增加公众号的关注一举两得。

1.授权流程图说明

使用Django实现微信公众号扫码登陆非OAuth2.0协议_第1张图片

在网页里实现微信扫码登陆,需要准备以下:

2.准备工作

1.一个认证好的微信公众号

2.微信扫码登陆步骤

你可能会用到微信测试公众账号

3.代码步骤

1.生成login_code

def createRandomString(len):
    import random
    #print ('wet'.center(10,'*'))
    raw = ""
    range1 = range(58, 65) # between 0~9 and A~Z
    range2 = range(91, 97) # between A~Z and a~z
    i = 0
    while i < len:
        seed = random.randint(48, 122)
        if ((seed in range1) or (seed in range2)):
            continue;
        raw += chr(seed);
        i += 1
    # print(raw)
    return raw

2.使用appid/secret信息获取tocken

def get_access_token():
    access_token = ''
    try:
        if cache.has_key('access_token') and cache.get('access_token') != '':
            access_token = cache.get('access_token')
            logging.critical('cache access_token:'+access_token)
        else:
            appId = APP_ID
            appSecret = APP_SECRET
            postUrl = ("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s" % (appId, appSecret))
            logging.debug(postUrl)
            urlResp = urllib.urlopen(postUrl).read()
            logging.critical(urlResp)
            urlResp = json.loads(urlResp)
            access_token = urlResp['access_token']
            cache.set('access_token', access_token, 60 * 100)
            #leftTime = urlResp['expires_in']
    except Exception, e:
        logging.critical(e.message)
    return access_token

3.使用tocken获取ticket

def get_qr_ticket(login_code):
    ticket = ''
    try:
#         if cache.has_key('ticket') and cache.get('ticket') != '':
#             ticket = cache.get('ticket')
#             logging.critical('cache ticket:'+ticket)
#         else:   
        token = get_access_token()
        logging.critical('get_access_token for ticket:'+token)
        data = {
            'expire_seconds': 604800,
            'action_name'   :'QR_STR_SCENE',
            'action_info'   : {
                'scene'     : {
                    'scene_str' : login_code
                }
        }}
        
        import requests as reqs
        params = json.dumps(data)
        #params = urllib.parse.urlencode(data).encode(encoding='UTF8')
        #headers = {'Accept-Charset': 'utf-8', 'Content-Type': 'application/json'}
        if token != '' :
            ticket_url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}".format(token)
            response = reqs.post(url=ticket_url, data=params)
            #response = reqs.urlopen(req).read()
            #get_qr_ticket = urllib.urlopen(ticket_url)
            #urlResp = get_qr_ticket.read().decode("utf-8")
            logging.critical(response.content)
            js_ticket = json.loads(response.content)
            ticket = js_ticket.get("ticket")
            #cache.set('ticket', ticket, 60 * 100)
        #r.setex('wx:ticket', ticket, 7200)
    except Exception as e:
        return ''
    return ticket

4.使用ticket显示出二维码图片

  • https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET 二维码地址
  • 显示图片内嵌 img标签, 并展示在浏览器中

5.用户扫码

def ajax_signin(request):
    #cache.set('access_token', '',1)
    from django.http import JsonResponse
    login_code = request.GET.get('login_code', None)#User.objects.filter(openid='123qwe')
    #logging.critical('login_code is :' + login_code)
    msg = ''
    next_url=''
    if login_code:
        #search unique user
        try:
            up = UserProfile.objects.get(login_code=login_code)
        except Exception:
            up = None
            
        #logging.critical('up is :' + str(up.pk))
        
        if up :
            user =User.objects.get(pk=up.pk)  
            if user:
                #no passwsod login
                user.backend = 'django.contrib.auth.backends.ModelBackend'
                login(request, user)
                msg = 'success'
                login_redirect_url = getattr(django_settings, 'LOGIN_REDIRECT_URL', None)
                next_url = get_next_url(request, default=login_redirect_url)
                if next_url == reverse('user_signin'):
                    next_url = '%(next)s?next=%(next)s' % {'next': next_url}
            #return HttpResponseRedirect(next_url)
    
    name_dict = {'msg':msg,'next_url':next_url}
    return JsonResponse(name_dict)
  • 扫码触发后,会有APP发送消息到微信后台
  • 微信后台接收到消息,将转发到服务端,通过平台配置的接口配置信息 返回数据
  •  在微信后台返回数据会包含用户的openid

         EventKey: session id,对应 步骤1中 login_code 参数信息
         FromUserName:扫码的用户openid

5.使用openid获取用户信息

def get_wx_userinfo(openid):
    bc_data = {}
    try:
        token = get_access_token()
        logging.critical('get_access_token for ticket:'+token)
        
        import requests as reqs
        if token != '' :
            ticket_url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={}&openid={}&lang=zh_CN".format(token,openid)
            response = reqs.post(url=ticket_url)
            logging.critical('get_wx_userinfo:'+response.content)
            bc_data = json.loads(response.content)
    except Exception as e:
        return ''
    return bc_data

6.保存用户信息

@csrf_exempt
def weixin_token(request):
    try:
        if request.method == 'GET' and request.GET.get('echostr') != '':
            import hashlib
            wechat_data = request.GET
            signature = wechat_data['signature']
            timestamp = wechat_data['timestamp']
            nonce = wechat_data['nonce']
            logging.info("handle/GET func: hashcode, signature:{0} {1}".format(signature, timestamp))
            echostr = wechat_data['echostr']
            #token = '35977a76fc8a3239867a67a62cf45f0d'
         
            check_list = [APP_TOKEN, timestamp, nonce]
            check_list.sort()
            s1 = hashlib.sha1()
            s1.update(''.join(check_list).encode())
            hashcode = s1.hexdigest()
            logging.debug("handle/GET func: hashcode, signature:{0} {1}".format(hashcode, signature))
            if hashcode == signature:
                return HttpResponse(echostr)
        else:
            postBody = request.body
            logging.critical(postBody)
            import xmltodict
            dictBody = xmltodict.parse(postBody)
            res = WXResponse(dictBody)
            type = res.check_event()
            
            logging.critical('type:'+type)
            
            #scan code
            if type == 'scan' or type == 'unsub_scan':
                openid = res.data.get("FromUserName")
                logging.critical('openid:'+openid)
                
                
                if type == 'unsub_scan':
                    login_code = res.data.get("EventKey").replace('qrscene_','')
                else:
                    login_code = res.data.get("EventKey")
                
                
                logging.critical('login_code:'+login_code)
                #search unique user
                try:
                    up = UserProfile.objects.get(openid=openid)
                except Exception:
                    up = None
                    
                #user =User.objects.get(pk=up.pk)
                # if has user
                if up :
                    UserProfile.objects.filter(openid=openid).update(login_code=login_code)
                else :
                    res = get_wx_userinfo(openid)
                    logging.critical('nickname:'+res['nickname'])
                    user = User()
                    #keep unique username
                    user.username = res['nickname'] + createRandomString(4)
                    user.set_unusable_password()
                    user.first_name = res['nickname']
                    user.last_name = ''
                    user.email = login_code + '@neui.net'
                    user.is_staff = False
                    user.is_superuser = False
                    user.is_active = True
                    user.save()
                    #avatar_urls=res['headimgurl'],
                    logging.critical('user.pk:'+str(user.pk))
                    #upn = UserProfile.objects.filter(pk=user.pk).update(openid = openid,login_code = login_code)
                    #logging.critical('upn:'+str(upn))
                    
                    up_obj = UserProfile.objects.get(pk=user.pk)
                    up_obj.openid = openid
                    up_obj.login_code = login_code
                    up_obj.save()
                    logging.critical('save data')
            
    except Exception, e:
        logging.critical(e.message)
    return HttpResponse("")

 

你可能感兴趣的:(微信,python)