微信网站扫一扫登录
个人blog+bbs www.youngboy.vip
上手须知
要想使用微信扫一扫就可以登录网站,首先要有微信开发平台账号,并且通过开发者资质认证(认证需要通过审核,审核费300人民币),然后添加网页应用准备网站信息登记表等信息提价审核,有公众号的可以绑定公众号,最终你会得到 appid 和 appsecret 然后就可以愉快的玩耍了
科普微信授权原理
微信用户->第三方应用: 请求登录第三方应用
第三方应用->微信开发平台: 请求oauth2授权
微信开发平台->微信用户: 请求用户确认
微信用户->微信开发平台: 用户确认
微信开发平台-> 第三方应用: 拉起第三方应用或重定向到第三方的服务器,并带上code
第三方应用 -> 微信开发平台: 通过code加上appid appsecret获取access_token
微信开发平台 -> 第三方应用: 返回用户相关信息
前端的打开方式
前端有两种打开方式
- 通过重定向到微信的页面然后用户扫描二维码授权,再重定向回第三方网站
- 通过js直接显示二维码登录,不用跳转微信页面
第一种方式是通过后台重定向过去的所以前端只需要写个链接就行了,第二种方式是在页面上直接显示二维码登录减少了跳页面的时间,这种方式需要在网页上用js实现,一般第二种用的比较多
使用js显示微信扫一扫二维码的打开方式
步骤1
:在页面中先引入如下JS文件(支持https):
https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
步骤2
:在需要使用微信登录的地方实例以下JS对象:
var obj = new WxLogin({
id:"login_container", // 需要显示的容器id
appid: "", // 公众号appid wx*******
scope: "snsapi_login", // 网页默认即可
redirect_uri: "", // 授权成功后回调的url
state: "", // 可设置为简单的随机数加session用来校验
style: "black", // 提供"black"、"white"可选。二维码的样式
href: "" // 外部css文件url,需要https
});
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
self_redirect | 否 | true:手机点击确认登录后可以在 iframe 内跳转到 redirect_uri,false:手机点击确认登录后可以在 top window 跳转到 redirect_uri。默认为 false。 |
id | 是 | 第三方页面显示二维码的容器id |
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
scope | 是 | 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可 |
redirect_uri | 是 | 重定向地址,需要进行UrlEncode |
state | 否 | 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验 |
style | 否 | 提供"black"、"white"可选,默认为黑色文字描述 |
href | 否 | 自定义样式链接,第三方可根据实际需求覆盖默认样式 |
用户点击确认授权后网页会自动跳转redirect_uri
前端扫码分析
https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js 的代码
!function(a, b, c) {
function d(a) {
var c = "default";
a.self_redirect === !0 ? c = "true": a.self_redirect === !1 && (c = "false");
var d = b.createElement("iframe"),
e = "https://open.weixin.qq.com/connect/qrconnect?appid=" + a.appid + "&scope=" + a.scope + "&redirect_uri=" + a.redirect_uri + "&state=" + a.state + "&login_type=jssdk&self_redirect=" + c;
e += a.style ? "&style=" + a.style: "",
e += a.href ? "&href=" + a.href: "",
d.src = e,
d.frameBorder = "0",
d.allowTransparency = "true",
d.scrolling = "no",
d.width = "300px",
d.height = "400px";
var f = b.getElementById(a.id);
f.innerHTML = "",
f.appendChild(d)
}
a.WxLogin = d
} (window, document);
上段代码主要功能是根据用户配置的消息在页面上嵌入一个iframe,我们看到的二维码是iframe和请扫描等提示语都是iframe中的
这里先给大家一个链接看看 点我
打开iframe的链接返回的是一个网页,再打开游览器的开发者工具可以看到一个长连接请求
https://long.open.weixin.qq.com/connect/l/qrconnect?uuid=081ciRVWnFkIOwL8&_=1534297144336
这个请求带有两个参数一个是 uuid
还有一个 是 _
看样子应该是时间戳,这个请求在没扫描之前一直是阻塞
的,如果27秒没有扫码会自动断开连接,并且返回一段js代码
window.wx_errcode=408;window.wx_code='';
再来看看网页中嵌入的一段代码,这里我省略了一些代码只留下比较重要的
!function() {
function a(d) {
jQuery.ajax({
type: "GET",
url: "https://long.open.weixin.qq.com/connect/l/qrconnect?uuid=011ZJt4N2TyhElXu" + (d ? "&last=" + d: ""),
dataType: "script", //这里是script类型
cache: !1,
timeout: 6e4,
success: function(d, e, f) {
var g = window.wx_errcode;//这里为什么是window.wx_errcode呢 因为返回的格式是script 内容是 window.wx_errcode=408;window.wx_code='';
switch (g) {
case 405://如果是405证明用户已经同意授权登录 用js重定向并带上code
var h = "http://www.jianshu.com/users/auth/wechat/callback";
h = h.replace(/&/g, "&"),
h += (h.indexOf("?") > -1 ? "&": "?") + "code=" + wx_code + "&state=123";
var i = b("self_redirect");
if (c) if ("true" !== i && "false" !== i) try {
document.domain = "qq.com";
var j = window.top.location.host.toLowerCase();
j && (window.location = h)
} catch(k) {
window.top.location = h
} else if ("true" === i) try {
window.location = h
} catch(k) {
window.top.location = h
} else window.top.location = h;
else window.location = h;
break;
case 404:
jQuery(".js_status").hide(),
jQuery("#wx_after_scan").show(),
setTimeout(a, 100, g);
break;
case 403:
jQuery(".js_status").hide(),
jQuery("#wx_after_cancel").show(),
setTimeout(a, 2e3, g);
break;
case 402:
case 500:
window.location.reload();
break;
case 408:
setTimeout(a, 2e3)
}
},
error: function(b, c, d) {
var e = window.wx_errcode;
408 == e ? setTimeout(a, 5e3) : setTimeout(a, 5e3, e)
}
})
}
...
} ();
上面的代码是一段自执行函数,链接一打开就会执行,在用户扫码之前请求是阻塞的success回调都不会执行,如果用户一直没有扫描二维码,请求在27秒内就会断开链接这时就会去执行success回调函数并且重新发起一个长连接重复以上步骤直到返回405
网页中的二维码解析后的结果是
https://open.weixin.qq.com/connect/confirm?uuid=0618LnlRCFd-jYeo
这个链接只有一个参数并且uuid和长连接请求的uuid是相同的,用游览器打开会提示Scope 参数错误或没有 Scope 权限,只能用微信打开才有用,微信的包不好抓希望有大神能够指点指点
后端的打开方式
重定向到微信页面的打开方式
登录你的网站应用
https://xxx.xxx.com/wechat/login.do
打开后需要重定向至微信的地址并带上必要的参数
https://open.weixin.qq.com/connect/qrconnect?
appid=wxbdc5610cc59c1631
&
redirect_uri=https%3A%2F%2Fxxx.xxx.com%2Fwechat%2Fcallback.do
&
response_type=code
&
scope=snsapi_login
&
state=3d6be0a4035d839573b04816624a415e#wechat_redirect
这里需要变的参数是appid和redirect_url
访问以上链接微信开发平台会重定向到redirect_uri的网址上,并且带上code
和state
参数,如果用户没有授权就不会有code
参数,仅会带上state参数
https://xxx.xxx.com?code=xxx&state=xxx
后台根据code获取用户信息封装
获取用户信息的步骤
- 通过code获取access_token
- 通过access_token调用接口
这里使用了weixin-java-tools
工具来获取用户信息
github地址 传送门
public WxMpUser getWxUser(String code) {
if (code == null) {
return null;
}
WxMpOAuth2AccessToken wxMpOAuth2AccessToken = null;
WxMpUser wxMpUser = null;
try {
wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
wxMpOAuth2AccessToken = wxMpService.oauth2refreshAccessToken(wxMpOAuth2AccessToken.getRefreshToken());
wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null);
} catch (WxErrorException e) {
return null;
}
return wxMpUser;
}
社区讨论
Java 长连接模拟微信登录
[图片上传失败...(image-6193f9-1557819001579)]
实现原理
使用Servlet3异步接口实现非阻塞长连接接口, 异步上下文对象使用 ScheduledExecutorService 线程池定时调度 事件总线使用了 guava 中的 EventBus 实现
有兴趣可以去瞧瞧,目前oauth2+openId扫码登录正在开发中... 欢迎fork添加代码