最近思否关注者激增几十人,感觉很是奇怪,想着可以给新关注用户发个问候信息之类。于是便想通过脚本的形式,给用户发送消息。但token获取是个问题,便想着也用脚本自动化。用户名密码登录会有验证码。那就使用微信第三方登录吧,正好巩固下OAuth。
1. 分析整个登录流程
Chrome隐私模式下,打开开发者模式,再打开https://segmentfault.com/
。
1.1 微信OAuth2.0授权地址获取
可以看到,默认登录方式就是微信登录。而微信OAuth地址获取入口为固定URL。这个同样也可以在JS源码中找到umi.js
。
地址为: https://segmentfault.com/gateway/session/oauth/weixin/redirect?is_mobile=0
再看该请求的返回数据:
{
"redirect_url":"https:\/\/open.weixin.qq.com\/connect\/qrconnect?appid=wxd8936345ec1df5ac&redirect_uri=https%3A%2F%2Fsegmentfault.com%2Fuser%2Foauth%2Fweixin&response_type=code&scope=snsapi_login#wechat_redirect"
}
得到redirect_url
为: https://open.weixin.qq.com/connect/qrconnect?appid=wxd8936345ec1df5ac&scope=snsapi_login&redirect_uri=https://segmentfault.com/user/oauth/weixin&state=&login_type=jssdk&self_redirect=default&styletype=&sizetype=&bgcolor=&rst=&href=https://s.segmentfault.com/css/weixin.css
我们解析一下该URL的参数:
appid: wxd8936345ec1df5ac
scope: snsapi_login
redirect_uri: https://segmentfault.com/user/oauth/weixin
state:
login_type: jssdk
self_redirect: default
styletype:
sizetype:
bgcolor:
rst:
href: https://s.segmentfault.com/css/weixin.css
可以发现,其实不通过SF
提供的地址也是可以的。至此已得到授权地址。
1.2 登录二维码获取
在Chrome上,会自动展示二维码。下面咱们分析下二维码的来源及展示内容。在浏览器上单独访问上文的redirect_url
。
先看下二维码的地址。https://open.weixin.qq.com/connect/qrcode/0913LA3008yh0w3q
。在看下原始网页中的内容。
我们可以发现,这一串内容在他们内部叫做uuid
。只要拿到这个uuid
就能拿到二维码。
在看一下二维码里面具体的内容。
https://open.weixin.qq.com/connect/confirm?uuid=031J67qn4WUy0w3b
可以看到,这个confirm
URL唯一重要的参数就是uuid
。拿到了UUID就拿到了二维码。
1.3 扫描结果在哪里
可以看到页面一直在做长轮询,间隔为15秒。返回结果:
window.wx_errcode=408;window.wx_code='';
地址为: https://lp.open.weixin.qq.com/connect/l/qrconnect?uuid=031J67qn4WUy0w3b&_=1669547560624
参数:
uuid
: 为授权请求二维码_
: 时间戳 (应该是避免缓存之类)
经过多次尝试分析发现。
目前已知wx_errcode:
- 402:二维码过期
- 404:已扫码(未操作)
- 405:扫码确认登录(有wx_code返回)
- 408:未扫码(初始状态)
- 初始值一直为
408
,然后长轮询。 - 当用户扫码后,返回值变为
404
,此时请求参数需要增加last=404
继续为止长轮询。 - 当用户确认后,返回
405
,此时wx_code
会有值。也就是OAuth协议中最重要的code
(该code
仅一次有效)
有了该code
,SF
便可以去微信获取用户数据(当然最重要的是openid
和unionid
)。便可根据该openid
获取到关联的用户,并生成该用户的token
并返回给前端。
1.4 登录接口(code换token)
1.4.1 方式一
在Chrome开发者工具中,可以看到请求:https://segmentfault.com/user/oauth/weixin?code=011hcEml2K03ka5gEWll2UUfkI0ckHQR&state=
。在其响应头中可以看到有set-cookie: PHPSESSID
。该PHPSESSID
即为token。
1.4.2 方式二
在Chrome开发者工具中,可以看到请求https://segmentfault.com/gateway/session/oauth/weixin/user
。参数为:
{
"type": "weixin",
"code": "011hcEml2K03ka5gEWll2UUfkI0ckHQR"
}
但是该接口却没有任何响应体及有效响应头。但通过不请求方式一
仅请求该接口时发现,响应头中有set-token
响应头,而内容即为token
。
2. 代码实现
外部库:
- requests: 用于发送请求
- qrcode_terminal: 用于在Terminal展示二维码
代码详见: https://gist.github.com/lpe234/f5bcb13ec03e3102357844e8a091e92c
执行结果:
2022-11-27 18:35:08,949 - sf_wx_login.py - INFO: [获取微信redirect_url] 尝试获取
2022-11-27 18:35:09,103 - sf_wx_login.py - INFO: [获取微信redirect_url] 获取成功 url=>https://open.weixin.qq.com/connect/qrconnect?appid=wxd8936345ec1df5ac&redirect_uri=https%3A%2F%2Fsegmentfault.com%2Fuser%2Foauth%2Fweixin&response_type=code&scope=snsapi_login#wechat_redirect
2022-11-27 18:35:09,103 - sf_wx_login.py - INFO: [获取微信qrcode] 尝试获取uuid
2022-11-27 18:35:09,436 - sf_wx_login.py - INFO: [获取微信qrcode] uuid获取成功 uuid=>0915eXbM1fPC1w3r
2022-11-27 18:35:09,437 - sf_wx_login.py - INFO: [获取微信qrcode] 准备渲染
[ ]
qr
[ ]
2022-11-27 18:35:10,469 - sf_wx_login.py - INFO: [长轮询登录结果] 开始轮询
2022-11-27 18:35:25,639 - sf_wx_login.py - INFO: [长轮询登录结果] 依旧未扫码,继续轮询
2022-11-27 18:35:28,107 - sf_wx_login.py - INFO: [长轮询登录结果] 已扫码,等待用户确认
2022-11-27 18:35:36,811 - sf_wx_login.py - INFO: [长轮询登录结果] 已扫码确认
2022-11-27 18:35:36,811 - sf_wx_login.py - INFO: [长轮询登录结果] 已扫码确认 wx_code=>041MyYZv3QDvEZ2boY3w44ksrY3MyYZf
2022-11-27 18:35:36,812 - sf_wx_login.py - INFO: [登录sf] 尝试登录
2022-11-27 18:35:37,378 - sf_wx_login.py - INFO: [登录sf] 返回数据 resp=>{'type': 'login', 'msg': '登录成功'}
2022-11-27 18:35:37,378 - sf_wx_login.py - INFO: [登录sf] 成功获取token token=>90f96559dc90c6c927752fd905cc3eed
2022-11-27 18:35:37,713 - sf_wx_login.py - INFO: [校验token] token有效, hello => lpe234