从2022年6月20号开始,企微API对于某些字段的返回策略进行了调整,不再默认返回头像、性别、手机等敏感信息。所以在开发企业微信扫码登录Web应用时,我们只能通过企微API获取用户ID(user_id),而头像、电话等敏感信息无法直接通过扫码API获取。
为了获取这些详细信息,我们需要通过OAuth2授权流程来获取用户的授权。
如果通过OAuth2获取用户详细,那么就不能跳转到@wecom-jssdk提供的扫码页面,也不能将企微API生成的二维码嵌入到自己的页面。而是需要自己构建登录二维码。
第一步,构建二维码
OAuth2授权URL的格式如下,将这个链接文字构建为二维码,用于客户手机扫码。
https://open.weixin.qq.com/connect/oauth2/authorize?appid=YOUR_APPID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=snsapi_base#wechat_redirect
前端可以使用qrious.js库构造二维码。也可以在后端渲染。
前端代码示例:
async function refreshqrcode(){
try{
res=await fetch('/qregain');
if(res.ok){
const { state } = await res.json();
//state的内容由后端生成,可以包含用来识别客户端的信息和其他内容,微信auth服务器会原样转发
url='https://open.weixin.qq.com/connect/oauth2/authorize?';
qrtext=url+'appid={
{data.appid}}&redirect_uri='+encodeURIComponent('{
{data.redirect_uri}}')+'&response_type=code&scope=snsapi_privateinfo&state='+state+'&agentid={
{data.agentid}}#wechat_redirect';
//双大括号是Jinja模板中表示插入变量的方式,js用`${}`
//scope=snsapi_privateinfo表示获取用户敏感信息
qrElement.parentNode.style.opacity='1.0';
//设置二维码img元素的父元素透明度为1.0,失效时=0.2
var qr = new QRious({
element: qrElement,
size:300,
value: qrtext
});//qriors.js库的用法
}
else{
consoloe.log(res.status);
}
}catch(error){
console.log('err');
//错误处理逻辑
}
}
第二步,等待扫码,接收Auth Code
在渲染二维码成功后,紧接着向后端发起一个HTTP请求waitForAuthCode,等待扫码的结果。这一步的作用是将Auth Code引人前端。企微手机端扫码后,手机直接向open.weixin.qq.com发起认证请求,微信认证服务器生成authcode成功后,它会重定向到redirect_uri指向的服务器,也就是我们自己的应用服务器。应用服务器对于这一重定向的响应回到手机端,手机端的这个页面响应可以设计成一个让客户确认的交互。可见,扫码这个过程中,没有数据再回到浏览器,那么就需要浏览器有一个获取Code的机制。
应用服务器已经在这一步获得了authcode,可以在设定的超时范围内将authcode响应给前端的waitForAuthCode。
前端:
async function waitForAuthCode() {
try {
const response = await fetch('/waitauthcode');
if (response.ok) {
const { authCode } = await response.json();
scanover=true;
window.location.href = 'https://example.com/signin?code='+authCode;
//取得authcode后,请求登录验证
}
}catch (error) {
//超时,刷新二维码
console.error('Error waiting for auth code:', error);
refreshqrcode();
}
}
后端以Flask为例:
import queue
@app.route('/waitauthcode')
def _check_code(*args):
"""
微信auth服务器返回的authcode进入以客户uid命名的队列globals()[uid]
"""
uid=request.cookies.get('uid')
q=globals()[uid]
try:
code=q.get(timeout=60) #取得authcode
return jsonify({'authCode': auth_code})
except:
q=None #超时的处理
return jsonify({'error': 'Timeout waiting for auth code'}), 408
第三步,请求获取用户身份
前端带code和state参数向应用服务器发起登录请求。服务器收到请求后, 请求获取用户身份。注意:auth code只能使用一次,所以后端必须有控制逻辑。
请求方式:GET(HTTPS)
请求地址:https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
参数说明:
参数 | 必须 | 说明 |
---|---|---|
access_token | 是 | 调用接口凭证 |
code | 是 | 通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 |
正确返回:
{
"errcode": 0,
"errmsg": "ok",
"userid":"USERID",
"user_ticket": "USER_TICKET"
}
第四步,获取用户敏感信息
请求方式:POST(HTTPS)
请求地址:https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail?access_token=ACCESS_TOKEN
请求包体:
{
"user_ticket": "USER_TICKET"
}
正确的返回结果:
{ "avatar": "https://wework.qpic.cn/wwpic3az/63*****9/0", "email": "someone@mail.qq.com", "errcode": 0, "errmsg": "ok", "gender": "0", "mobile": "1********00", "name": "\u****\u****", "qr_code": "https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=vcac*****d", "userid": "001" }
最后,附上顺序图: