Flask--网页微信登陆示例

流程分析

https://wx.qq.com/页面
这里写图片描述

<div class="qrcode" ng-class="{hide: isScan || isAssociationLogin || isBrokenNetwork}">
                <img class="img" mm-error="qrcodeException()" ng-class="{'qrcode_expired': isNeedRefresh}" mm-src="https://login.weixin.qq.com/qrcode/Ie_lVu7qmQ==" mm-src-load="qrcodeLoad" mm-src-parallel="" mm-src-timeout="10" mm-src-retry-count="2" src="https://login.weixin.qq.com/qrcode/Ie_lVu7qmQ==">
                <div ng-show="!isNeedRefresh" class="">
                    <p class="sub_title">使用手机微信扫码登录</p>
                    <p class="sub_desc">网页版微信需要配合手机使用</p>
                </div>
                <div ng-show="isNeedRefresh" class="ng-hide">
                    <div class="refresh_qrcode_mask">
                        <i class="icon-refresh" ng-class="{rotateLoading: isRotateLoading}" ng-click="refreshQrcode()"></i>
                    </div>
                    <p class="refresh_tips">二维码失效,点击刷新</p>
                </div>
            </div>

我们看下src="https://login.weixin.qq.com/qrcode/Ie_lVu7qmQ=="图片式如何生成的呢?

由此我们得知web微信,在输出https://wx.qq.com/时候,会发起https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_=1525767248719请求,返回window.QRLogin.code = 200; window.QRLogin.uuid = "Ie_lVu7qmQ==";响应,
返回的response如下:

window.QRLogin.code = 200; window.QRLogin.uuid = "Ie_lVu7qmQ==";

然后拿到uuid,这里式Ie_lVu7qmQ==,然后渲染到html生成图片

同时在像微信服务器发起如下请求,

https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=wbvMiDtJMw==&tip=1&r=-1055033147&_=1525768422635

然后进行检测,看是否有手机扫码登陆,微信服务器处于pending状态,一直在等待手机扫码登陆,如果在某个时间段(大概半分钟),没有手机扫码登陆,就返回408状态码,然后再次微信服务器处于pending状态

以下式window.code=408;响应截图

这里写图片描述

这里写图片描述

然后用手机扫码操作

ret = requests.get(check_url)
print("ret.text~~~~~~~",ret.text)

输出如下

ret.text~~~~~~~ window.code=201;window.userAvatar = 'data:img/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0llSiiGBDoooomB/9k=';

然后在不登陆的情况下,依然式pending状态
这里写图片描述

手机端点击登陆,返回如下
这里写图片描述

window.code=200;
window.redirect_uri="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=AbczicMqBfNXxYuJs5G0CbRy@qrticket_0&uuid=oamap_bsaQ==&lang=zh_CN&scan=1525770582";

然后会继续在redirect_uri发请求

https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=ARsBaAiUYgJbySWbtovgZyVd@qrticket_0&uuid=Ibo3sJnmJQ==&lang=zh_CN&scan=1525771779&fun=new&version=v2&lang=zh_CN

这里写图片描述
返回结果如下:
这里写图片描述
返回的结果式xml格式:

<error>
<ret>0</ret>
<message></message>
<skey>@crypt_56c6e845_49bcf817dfc65bb4142170e355cf0ab2</skey
><wxsid>W7TXjDJ6pmok5Sbm</wxsid>
<wxuin>2404580303</wxuin>
<pass_ticket>OHHPpM8kNYzmK7XrKaYDJ3OtO7HldKT2REIuj148Fs%2FKNXEIOVKz0Ip%2FW6HbYgh</pass_ticket>
<isgrayscale>1</isgrayscale>
</error>

然后带着xml进行数据初始化:
这里写图片描述

然后渲染页面信息即可

示例代码

from flask import Flask, request, render_template, session, jsonify
import time
import requests
import re
from bs4 import BeautifulSoup
import json

app = Flask(__name__)
app.debug = True
app.secret_key = 'asdf3sdfsdf'


def xml_parser(text):
    dic = {}
    soup = BeautifulSoup(text, 'html.parser')
    div = soup.find(name='error')
    for item in div.find_all(recursive=False):
        dic[item.name] = item.text
    return dic


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        ctime = str(int(time.time() * 1000))
        qcode_url = "https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_={0}".format(
            ctime)

        ret = requests.get(qcode_url)
        qcode = re.findall('uuid = "(.*)";', ret.text)[0]
        session['qcode'] = qcode
        return render_template('login.html', qcode=qcode)
    else:
        pass


@app.route('/check_login')
def check_login():
    """
    发送GET请求检测是否已经扫码、登录
    https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=QbeUOBatKw==&tip=0&r=-1036255891&_=1525749595604
    :return:
    """
    response = {'code': 408}
    qcode = session.get('qcode')
    ctime = str(int(time.time() * 1000))
    check_url = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip=0&r=-1036255891&_={1}".format(
        qcode, ctime)
    ret = requests.get(check_url)
    print("ret.text~~~~~~~", ret.text)
    if "code=201" in ret.text:
        # 扫码成功
        src = re.findall("userAvatar = '(.*)';", ret.text)[0]
        response['code'] = 201
        response['src'] = src
    elif 'code=200' in ret.text:
        # 确认登录
        print("code=200~~~~~~~", ret.text)
        redirect_uri = re.findall('redirect_uri="(.*)";', ret.text)[0]

        # 向redirect_uri地址发送请求,获取凭证相关信息
        redirect_uri = redirect_uri + "&fun=new&version=v2"
        ticket_ret = requests.get(redirect_uri)
        ticket_dict = xml_parser(ticket_ret.text)
        session['ticket_dict'] = ticket_dict
        response['code'] = 200
    return jsonify(response)


@app.route('/index')
def index():
    """
    用户数据的初始化
    https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-1039465096&lang=zh_CN&pass_ticket=q9TOX4RI4VmNiHXW9dUUl1oMzoQK2X2f3H3kn0VYm5YGNwUMO2THYMznv8DSXqp0

    :return:
    """
    ticket_dict = session.get('ticket_dict')
    init_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-1039465096&lang=zh_CN&pass_ticket={0}".format(
        ticket_dict.get('pass_ticket'))

    data_dict = {
        "BaseRequest": {
            "DeviceID": "e750865687999321",
            "Sid": ticket_dict.get('wxsid'),
            "Uin": ticket_dict.get('wxuin'),
            "Skey": ticket_dict.get('skey'),
        }
    }

    init_ret = requests.post(
        url=init_url,
        json=data_dict
    )
    init_ret.encoding = 'utf-8'
    user_dict = init_ret.json()
    print(user_dict)
    # for user in user_dict['ContactList']:
    #     print(user.get('NickName'))

    return render_template('index.html', user_dict=user_dict)


if __name__ == '__main__':
    app.run()

login页面

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>
    <div style="width: 200px;margin: 0 auto">
        <h1 style="text-align: center">微信登录</h1>
        <img id="img" style="height: 200px;width: 200px;" src="https://login.weixin.qq.com/qrcode/{{qcode}}" alt="">
    </div>

    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        $(function () {
            checkLogin();
        })

        function checkLogin() {
            $.ajax({
                url:'/check_login',
                type:'GET',
                dataType:'JSON',
                success:function (arg) {
                    if(arg.code === 201){
                        // 扫码
                        $('#img').attr('src',arg.src);
                        checkLogin();
                    }else if(arg.code === 200){
                        // 重定向到用户列表
                        location.href = '/index'
                    }else{
                        checkLogin();
                    }

                }
            })
        }

    </script>

</body>
</html>

index.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>
    <h1>欢迎登录:{{user_dict.User.NickName}}</h1>
    <h3>最近联系人</h3>
    <ul>
        {% for user in user_dict.ContactList%}
        <li>{{user.NickName}}</li>
        {% endfor %}
    </ul>
</body>
</html>

测试效果图:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/u013210620/article/details/80241990