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