django网站接入微信登录,在手机非微信浏览器端登录,用h5唤起微信客户端登录,总提示:请在微信客户端打开链接呢?

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在手机端唤起微信客户端登录?谢谢

阅读 594
avatarAI BotBETA

问题分析:

出现 "请在微信客户端打开链接" 的根本原因是:你使用了错误的授权端点或参数组合。以下是关键错误点和解决方案:


错误 1:H5 登录的授权 URL 错误

# 你的代码中使用了:
wechat_auth_url = "https://open.weixin.qq.com/connect/oauth2/authorize"

问题oauth2/authorize 是微信公众号的授权端点,只能在微信浏览器内使用。非微信浏览器的 H5 登录必须使用微信开放平台专用端点

修复

# 手机端浏览器登录(使用开放平台的 H5 登录)
wechat_auth_url = (
    f"https://open.weixin.qq.com/connect/qrconnect"  # ← 关键修改!
    f"?appid={settings.WECHAT_OPEN_APP_ID}"
    f"&redirect_uri={settings.WECHAT_OPEN_REDIRECT_URI}"
    f"&response_type=code"
    f"&scope=snsapi_login"  # ← 必须用 snsapi_login
    f"&state={state}"
    f"#wechat_redirect"
)

错误 2:JS-SDK 方案无效

你的 方式2 尝试用 wx.launchApplication 唤醒微信,但微信官方已明确不支持通过 JS-SDK 直接唤起微信登录。此方案不可行,应完全移除。


其他必检项

  1. 开放平台配置

    • 登录 微信开放平台 → 应用详情 → 开发设置 → 授权回调域 → 确保填写了你的网站域名(如 www.yoursite.com)。
    • 必须完成开发者资质认证,否则 H5 登录功能无法使用。
  2. redirect_uri 编码问题

    # 确保 redirect_uri 是 URL 编码后的值
    from urllib.parse import quote
    redirect_uri_encoded = quote(settings.WECHAT_OPEN_REDIRECT_URI)

    在生成 URL 时使用编码后的 redirect_uri_encoded

  3. 作用域混淆

    • 微信公众号(微信内浏览器):用 snsapi_userinfo
    • 开放平台 H5 登录:必须用 snsapi_login

修正后的完整流程

1. 前端点击按钮直接跳转:

<script>
document.getElementById('wechat-login').addEventListener('click', function () {
    // 判断设备类型
    const isMobile = /mobile|android|iphone/i.test(navigator.userAgent);
    
    if (isMobile) {
        // 手机端直接跳转 H5 登录 URL
        window.location.href = "/user/wechat/login/"; 
    } else {
        // PC 端处理(略)
    }
});
</script>

2. 后端视图修正:

def wechat_login(request):
    state = generate_state()
    request.session['wechat_state'] = state

    user_agent = request.META.get('HTTP_USER_AGENT', '').lower()
    is_wechat_browser = 'micromessenger' in user_agent
    is_mobile = any(kw in user_agent for kw in ['mobile', 'android', 'iphone'])

    if is_wechat_browser:
        # 微信内授权(公众号)
        url = f"https://open.weixin.qq.com/connect/oauth2/authorize?..."
    elif is_mobile:
        # H5 登录(开放平台)
        redirect_uri = quote(settings.WECHAT_OPEN_REDIRECT_URI)  # 编码
        url = (
            f"https://open.weixin.qq.com/connect/qrconnect"
            f"?appid={settings.WECHAT_OPEN_APP_ID}"
            f"&redirect_uri={redirect_uri}"
            f"&response_type=code"
            f"&scope=snsapi_login"
            f"&state={state}#wechat_redirect"
        )
    else:
        # PC 扫码登录(略)
    
    return redirect(url)

最终效果

  • 手机非微信浏览器 → 跳转到微信官方的 H5 登录页面(用户需手动点击「打开微信」按钮)。
  • 微信浏览器内 → 静默授权。
  • PC 端 → 展示二维码。

若仍提示错误,请按以下顺序排查:

  1. 开放平台应用是否通过审核并开通了「微信登录」功能。
  2. redirect_uri 是否与开放平台配置的域名完全一致(含 http/https)。
  3. 服务器 IP 是否加入开放平台的 IP 白名单。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
极客观点
子站问答
访问
宣传栏