V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
python30
V2EX  ›  Python

django 网站接入微信登录,在手机非微信浏览器端登录,想用微信开放平台的 h5 唤起微信客户端登录,代码如下,怎么还是总提示:请在微信客户端打开链接呢?

  •  
  •   python30 · 18 天前 · 1148 次点击

    django 网站接入微信登录,在手机非微信浏览器端登录,想用微信开放平台的 h5 唤起微信客户端登录,代码如下,怎么还是总提示:请在微信客户端打开链接呢?

    views.py

    def generate_state():
        """
        生成一个随机的 state 参数
        """
        return secrets.token_urlsafe(16)  # 生成一个 16 字节的随机字符串
    
    def wechat_login(request):
        """
        微信登录入口,根据设备类型选择不同的授权方式
        """
        user_agent = request.META.get('HTTP_USER_AGENT', '').lower()
    
        # 判断是否为微信浏览器
        is_wechat_browser = 'micromessenger' in user_agent
    
        # 判断是否为手机端
        is_mobile = any(keyword in user_agent for keyword in ['mobile', 'android', 'iphone', 'ipod'])
    
        # 生成随机的 state 参数
        state = generate_state()
        request.session['wechat_state'] = state  # 将 state 存储在会话中
    
    
        if is_wechat_browser:
            # 微信浏览器内登录(使用微信公众号的 OAuth2.0 授权)
            wechat_auth_url = (
                f"https://open.weixin.qq.com/connect/oauth2/authorize"
                f"?appid={settings.WECHAT_MP_APP_ID}"  # 使用微信公众号的 AppID
                f"&redirect_uri={settings.WECHAT_MP_REDIRECT_URI}"  # 微信公众号的回调地址
                f"&response_type=code"
                f"&scope=snsapi_userinfo"  # 使用 snsapi_userinfo 或 snsapi_base
                f"&state={state}#wechat_redirect"
            )
        elif is_mobile:
            # 手机端浏览器登录(使用微信开放平台的 H5 登录)
            wechat_auth_url = (
                f"https://open.weixin.qq.com/connect/oauth2/authorize"
                f"?appid={settings.WECHAT_OPEN_APP_ID}"  # 使用微信开放平台的 AppID
                f"&redirect_uri={settings.WECHAT_OPEN_REDIRECT_URI}"  # 微信开放平台的回调地址
                f"&response_type=code"
                f"&scope=snsapi_userinfo"  # 使用 snsapi_login
                f"&state={state}#wechat_redirect"
            )
        else:
            # 电脑端浏览器登录(使用微信开放平台的扫码登录)
            wechat_auth_url = (
                f"https://open.weixin.qq.com/connect/qrconnect"
                f"?appid={settings.WECHAT_OPEN_APP_ID}"  # 使用微信开放平台的 AppID
                f"&redirect_uri={settings.WECHAT_OPEN_REDIRECT_URI}"  # 微信开放平台的回调地址
                f"&response_type=code"
                f"&scope=snsapi_login"  # 使用 snsapi_login
                f"&state={state}#wechat_redirect"
            )
    
        return redirect(wechat_auth_url)
    
    
    def get_wechat_h5_login_url(request):
        """
        生成微信开放平台的 H5 登录 URL
        """
    	# 生成随机的 state 参数
        state = generate_state()
        request.session['wechat_state'] = state
        # 微信开放平台的 H5 登录 URL
        wechat_h5_login_url = (
            f"https://open.weixin.qq.com/connect/oauth2/authorize"
            f"?appid={settings.WECHAT_OPEN_APP_ID}"  # 微信开放平台的 AppID
            f"&redirect_uri={settings.WECHAT_OPEN_REDIRECT_URI}"  # 回调地址
            f"&response_type=code"
            f"&scope=snsapi_login"  # 使用 snsapi_login
            f"&state={state}"  # 可选参数,用于防止 CSRF 攻击
            f"#wechat_redirect"
        )
    
        return JsonResponse({
            'success': True,
            'login_url': wechat_h5_login_url,
        })
    
    
    def filter_username(nickname):
        """
        过滤掉不允许的字符,只保留字母、数字、_、-
        """
        # 只保留字母、数字、_、-
        filtered = re.sub(r'[^\w-]', '', nickname)
        return filtered
    
    def generate_unique_username(nickname):
        """
        根据微信昵称生成唯一的用户名
        """
        # 过滤特殊字符
        base_username = filter_username(nickname)
        
        # 如果过滤后的用户名为空,使用默认用户名
        if not base_username:
            base_username = 'wechat_user'
        
        # 添加随机后缀
        random_suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=4))
        username = f"{base_username}_{random_suffix}"
        
        # 检查用户名是否已存在
        while User.objects.filter(username=username).exists():
            random_suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=4))
            username = f"{base_username}_{random_suffix}"
        
        return username
    
     
    def wechat_callback(request):
        """
        微信登录回调处理
        """
        code = request.GET.get('code')
        state = request.GET.get('state')
        if not code:
            return HttpResponse('授权失败,未获取到 code')
    
        # 验证 state 参数
        if state != request.session.get('wechat_state'):
            return HttpResponse('非法请求,state 参数不匹配')
    	
        # 清除会话中的 state
        request.session.pop('wechat_state', None)
    
    
        # 判断是否为微信浏览器
        user_agent = request.META.get('HTTP_USER_AGENT', '').lower()
        is_wechat_browser = 'micromessenger' in user_agent
    
        # 根据设备类型选择 appid 和 secret
        if is_wechat_browser:
            # 微信浏览器内登录(使用微信公众号的 appid 和 secret )
            appid = settings.WECHAT_MP_APP_ID
            secret = settings.WECHAT_MP_APP_SECRET
        else:
            # 非微信浏览器登录(使用微信开放平台的 appid 和 secret )
            appid = settings.WECHAT_OPEN_APP_ID
            secret = settings.WECHAT_OPEN_APP_SECRET
    
        # 通过 code 获取 access_token
        token_url = (
            f"https://api.weixin.qq.com/sns/oauth2/access_token"
            f"?appid={appid}"
            f"&secret={secret}"
            f"&code={code}"
            f"&grant_type=authorization_code"
        )
        response = requests.get(token_url)
        data = response.json()
    
        access_token = data.get('access_token')
        openid = data.get('openid')
        unionid = data.get('unionid')  # 获取 unionid
    
        if not access_token or not openid:
            return HttpResponse('获取 access_token 失败')
    
        # 获取用户信息
        user_info_url = (
            f"https://api.weixin.qq.com/sns/userinfo"
            f"?access_token={access_token}"
            f"&openid={openid}"
        )
        response = requests.get(user_info_url)
        response.encoding = 'utf-8'
        user_info = response.json()
    
        # 根据 unionid 查找或创建用户
        user, created = User.objects.get_or_create(unionid=unionid)
        if created:
            # 生成唯一的用户名
            nickname = user_info.get('nickname', '微信用户')
            user.username = generate_unique_username(nickname)
            user.set_unusable_password()  # 微信登录用户不需要密码
        user.nickname = user_info.get('nickname', '')  # 更新昵称
        user.avatar = user_info.get('headimgurl', '')  # 更新头像
        user.wechat_nickname = user_info.get('nickname', '')  # 更新微信昵称
        user.save()
    
        # 登录用户
        login(request, user)
    
        return redirect('/')  # 登录成功后跳转到首页
    
    
    def get_js_sdk_config(request):
        """
        获取 JS-SDK 配置
        """
        try:
            access_token = get_access_token(settings.WECHAT_MP_APP_ID, settings.WECHAT_MP_APP_SECRET)
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=500)
    
        # 获取 JSAPI Ticket
        ticket_url = f"https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={access_token}&type=jsapi"
        response = requests.get(ticket_url)
        jsapi_ticket = response.json().get('ticket')
    
        # 生成签名参数
        nonce_str = generate_nonce_str()
        timestamp = int(time.time())
        url = request.build_absolute_uri()
    
        # 生成签名
        signature = generate_signature(jsapi_ticket, nonce_str, timestamp, url)
    
        # 返回 JS-SDK 配置
        return JsonResponse({
            'appId': settings.WECHAT_MP_APP_ID,  # 公众号的 AppID
            'timestamp': timestamp,
            'nonceStr': nonce_str,
            'signature': signature,
            'wechatOpenAppId': settings.WECHAT_OPEN_APP_ID,  # 微信开放平台的 AppID
            'wechatOpenRedirectUri': settings.WECHAT_OPEN_REDIRECT_URI,  # 微信开放平台回调地址
    		'wechatMpRedirectUri': settings.WECHAT_MP_REDIRECT_URI  # 微信开放平台回调地址
        })
    
    def get_access_token(appid, appsecret):
        """
        获取微信 access_token ,并缓存
        """
        # 先从缓存中获取 access_token
        access_token = cache.get('wechat_access_token')
        if access_token:
            return access_token
    
        # 缓存中没有,则从微信接口获取
        url = f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={appsecret}"
        response = requests.get(url)
        data = response.json()
        
        if 'access_token' in data:
            access_token = data['access_token']
            # 将 access_token 缓存 7000 秒(微信的有效期是 7200 秒)
            cache.set('wechat_access_token', access_token, 7000)
            return access_token
        else:
            raise Exception(f"获取 access_token 失败: {data}")
    

    urls.py

    		path('wechat/login/', wechat_login, name='wechat_login'),
    		path('wechat/callback/', wechat_callback, name='wechat_callback'),
    		path('get_js_sdk_config/', get_js_sdk_config, name='get_js_sdk_config'),  # 获取 JS-SDK 配置
            path('wechat/get_wechat_h5_login_url/', get_wechat_h5_login_url, name='get_wechat_h5_login_url'),#手机端非微信浏览器登录
    

    login.html 方式 1

    <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
    <div class="container reg">
        <!-- 微信登录按钮 -->
        <button id="wechat-login">微信登录</button>
    </div>
    <script>
        document.getElementById('wechat-login').addEventListener('click', function () {
            // 判断是否为微信浏览器
            var userAgent = navigator.userAgent.toLowerCase();
            var isWechatBrowser = userAgent.indexOf('micromessenger') !== -1;
            var isMobile = /mobile|android|iphone/i.test(userAgent);
    
            if (isWechatBrowser || !isMobile) {
                // 微信浏览器内或电脑端,直接跳转到后端生成的微信登录 URL
                window.location.href = "/user/wechat/login/"; // 这里假设 Django 反向解析后的实际 URL
            } else {
                // 手机端非微信浏览器,使用微信开放平台的 H5 登录
                // 向后端请求微信开放平台的 H5 登录 URL
                var xhr = new XMLHttpRequest();
                xhr.open('GET', '/user/wechat/get_wechat_h5_login_url/', true); // 后端生成 H5 登录 URL 的接口
                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4 && xhr.status === 200) {
                        var response = JSON.parse(xhr.responseText);
                        if (response.success) {
                            // 跳转到微信开放平台的 H5 登录页面
                            window.location.href = response.login_url;
                        } else {
                            alert('获取微信登录链接失败,请稍后重试。');
                        }
                    }
                };
                xhr.send();
            }
        });
    </script>
    

    login.html 方式 2

    <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
    <div class="container reg">
        <!-- 微信登录按钮 -->
        <button id="wechat-login">微信登录</button>
    </div>
    <script>
            document.getElementById('wechat-login').addEventListener('click', function () {
                // 判断是否为微信浏览器
                var userAgent = navigator.userAgent.toLowerCase();
                var isWechatBrowser = userAgent.indexOf('micromessenger')!== -1;
                var isMobile = /mobile|android|iphone/i.test(userAgent);
    
                if (isWechatBrowser ||!isMobile) {
                    // 微信浏览器内或电脑端,直接跳转到后端生成的微信登录 URL
                    window.location.href = "/user/wechat/login/"; // 这里假设 Django 反向解析后的实际 URL
                } else {
                    // 获取 JS-SDK 配置
                    var xhr = new XMLHttpRequest();
                    xhr.open('GET', '/user/get_js_sdk_config/', true);
                    xhr.onreadystatechange = function () {
                        if (xhr.readyState === 4 && xhr.status === 200) {
                            var config = JSON.parse(xhr.responseText);
                            wx.config({
                                debug: true, // 调试模式
                                appId: config.appId, // 公众号的 AppID
                                timestamp: config.timestamp, // 时间戳
                                nonceStr: config.nonceStr, // 随机字符串
                                signature: config.signature, // 签名
                                jsApiList: ['launchApplication'] // 需要使用的 JS 接口
                            });
    
                            wx.ready(function () {
                                // 尝试唤醒微信客户端
                                wx.launchApplication({
                                    appId: config.appId, // 这里需要替换为实际的微信 AppID
                                    extraData: '',
                                    success: function () {
                                        // 唤醒成功,跳转到微信登录 URL
                                        window.location.href = "/user/wechat_login/"; // 这里假设 Django 反向解析后的实际 URL
                                    },
                                    fail: function (res) {
                                        alert('唤醒微信客户端失败,请检查您的微信是否安装或尝试其他登录方式。');
                                    }
                                });
                            });
    
                            wx.error(function (res) {
                                alert('JS-SDK 配置出错,请刷新页面重试。');
                            });
                        }
                    };
                    xhr.send();
                }
            });
        </script>
    

    setting.py 里面也配置好了上面的 appid 相关参数与回调地址,并且在电脑 pc 端,微信浏览器内都可以成功登录了,就是在手机非浏览器端,不管怎么操作,都是提示:请在微信客户端内打开链接

    麻烦各位大佬看看是哪里的原因? 一直无法用 h5 的 js-sdk 在手机端唤起微信客户端登录?谢谢

    2 条回复    2025-02-23 17:47:44 +08:00
    xavierchow
        1
    xavierchow  
       18 天前   ❤️ 1
    python 不是很熟,粗粗看了一下,如果你想要在非微信浏览器用微信登录的话,应该是快速登录功能: https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html

    你现在判断到手机端非微信浏览器的时候,调用 get_wechat_h5_login_url 去拿重定向的地址,但是里面的 API 用错了,https://open.weixin.qq.com/connect/oauth2/authorize 这个是公众号网页授权登录(需要在微信浏览器内);你要唤起微信做快速登录是这个 https://open.weixin.qq.com/connect/qrconnect API ,具体流程你可以参考👆贴的官方快速登录功能的文档。
    python30
        2
    python30  
    OP
       17 天前
    @xavierchow 谢谢,我换了这个接口后,跟在电脑 pc 端一样了,生成了一个二维码,让扫码登录,我看别的手机端网站,点微信登录,可以跟在微信浏览器内一样,确认一下就直接微信登录了,以前有 wx.launchApplication 唤醒微信登录,后来取消这个了,现在也不知道用啥办法了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5210 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 08:22 · PVG 16:22 · LAX 01:22 · JFK 04:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.