本文主要是PC的web应用系统(网站应用)整合微信的扫码登录逻辑,即通过微信扫码完成业务系统的登录,也会把常见的几种模式概括性的带一下(当然仅限于自己理解的形式)。
技术栈还是spring+vue+elementUI全家桶。
当前看到的微信扫码登录的有以下两种模式:
公众平台提供了生成带参数二维码的接口。使用该接口可以获得多个带不同场景值的二维码,用户扫描后,公众号可以接收到事件推送。
简单理解:就是微信公众平台提供了相应接口,扫码之后不管用户关注与否都会有对应的回调通知给开发者,开发者可以此来做对应的业务开发。
本文是以微信开发平台的网站应用微信登录的方式实现的
在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。
根据官方文档的说法,获取显示二维码也有两种方式:
1、直接打开微信官方提供的链接,以新页面的形式。
2、内嵌到页面中显示,一般为了业务系统整体UI的协调和好看,都会采用此种方式。
用户扫码授权通过之后,主要是为了获取到code参数,以便后面的接口调用
通过访问以下链接,配置好对应的参数,即可获取到二维码:
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
其中比较重要的两个参数:
redirect_uri---->授权成功后的重定向地址,会带上code参数,如何配置后面会说说个人理解。
state---->访问重定向地址时会返回,可用于做接口校验。
正常操作的正确返回:redirect_uri?code=CODE&state=STATE
需要引入微信官方提供的js文件,在js中按照要求实例对象即可。通过查看微信官方提供的js文件源码可知,其实就是以iframe的形式访问方式一的地址。部分源码分析如下(若需要修改二维码的属性可直接修改js):
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 + '&styletype=' + (a.styletype || '') + '&sizetype=' + (a.sizetype || '') + '&bgcolor=' + (a.bgcolor || '') + '&rst=' + (a.rst || '');
e += a.style ? "&style=" + a.style: "",
e += a.href ? "&href=" + a.href: "",
d.src = e,
有现成的vue插件可以直接使用,vue-wxlogin组件首页地址,具体代码结构如下,其实也是构建了一个iframe的形式显示二维码,后面如何与业务系统整合基于此方式说明:
<template>
<div>
<iframe scrolling="no" width="300" height="400" frameBorder="0" allowTransparency="true" :src="setSrc"></iframe>
</div>
</template>
<script>
export default {
name:'wxlogin',
data () {
return {
src: 'https://open.weixin.qq.com/connect/qrconnect?appid=wxe1f5def243e0390b&scope=snsapi_login&redirect_uri=https://abstest.tenpay.com/abs/author/callBack.do&state=0001&login_type=jssdk&self_redirect=default&style=black&href=./wx.css',
}
},
computed : {
setSrc () {
var _url = 'https://open.weixin.qq.com/connect/qrconnect?appid='+ this.appid
+ '&scope=' + this.scope
+ '&redirect_uri=' + this.redirect_uri
+ '&state=' + this.state
+ '&login_type=' + this.login_type
+ '&self_redirect=' + this.self_redirect
+ '&style=' + this.theme
+ '&href=' + this.href;
return _url;
},
},
props:{
//应用唯一标识,在微信开放平台提交应用审核通过后获得
appid : String,
//应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
scope : String,
//重定向地址,需要进行UrlEncode
redirect_uri : String,
//用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验
state : {
type : String,
default: ''
},
//提供"black"、"white"可选,默认为黑色文字描述。详见文档底部FAQ
theme : {
type : String,
default: 'black'
},
// 自定义样式链接,第三方可根据实际需求覆盖默认样式。详见文档底部FAQ
href : {
type : String,
default: ''
},
// true:手机点击确认登录后可以在 iframe 内跳转到 redirect_uri,false:手机点击确认登录后可以在 top window 跳转到 redirect_uri。默认为 false。
self_redirect : {
type : Boolean,
default: false
},
// sdk的扩展字符串,但是在这里就默认了jssdk,暂时不建议修改
login_type : {
type : String,
default: 'jssdk'
},
},
}
</script>
在文件中使用:
<template>
<div>
<wxlogin :appid="me_appid" :scope="me_scope" :redirect_uri="me_redirect_uri" :self_redirect="true"></wxlogin>
</div>
</template>
<script>
import wxlogin from '../components/vue-wxlogin.vue';
export default {
components:{wxlogin},
data(){
return{
me_appid:'',
me_scope:'',
me_redirect_uri:'',
},
mmounted(){
//……do something
}
}
}
此时,页面中已经可以正常显示微信授权登录的二维码信息了。用户可扫码确认授权或者不授权。
不管采用哪种redirect_uri的配置,服务端都需要接收code参数,然后服务端主要做了三个事情:
1、根据code换取access_token(接口调用凭证)
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
会返回用户的openid和access_token两个关键参数
2、使用access_token和openid获取用户信息,
http请求方式: GET
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
具体响应的用户参数可查看授权后接口调用的文档说明。
3、根据用户信息,做对应的业务处理(比如:查询业务系统是否有对应的用户)。
当然也可以再服务端获取state参数,做一下接口检验,保障传输的正确性
@RequestMapping(value = "wx_login",method=RequestMethod.GET)
public String redirectUri(@RequestParam(name = "code", required = false) String code) throws ClientProtocolException, IOException {
//获取接口调用凭证
String AccessToken_url = WxLoginParams.WX_ACCESS_TOKEN+"?appid="+WxLoginParams.WX_OPEN_APPID+"&secret="+WxLoginParams.WX_OPEN_APPSECRET+"&code="+code+"&grant_type=authorization_code";
//HttpClientUtils为服务端访问https接口的工具类
String access_token_str = HttpClientUtils.doGet(AccessToken_url);
System.out.println(access_token_str);
//格式化获取凭证接口响应值。
JSONObject jo_token = JSON.parseObject(access_token_str);
String tocken = jo_token.getString("access_token");
String openid = jo_token.getString("openid");
//根据接口凭证获取用户信息。
String wx_userinfo_url = WxLoginParams.WX_USERINFO_URL+"?access_token="+tocken+"&openid="+openid;
String wx_userinfo = HttpClientUtils.doGet(wx_userinfo_url);
//格式化用户信息
JSONObject jo_user = JSON.parseObject(wx_userinfo);
//可根据格式化的用户信息来做相应的业务逻辑处理。
//......do something
return wx_userinfo;
}
要和业务系统整合,还得看具体的业务场景和使用的开发框架,本文暂时还是以我项目的场景记录一下。
其中比较关键的是关于redirect_uri的配置,决定着如何整合。
首先代码中的redirect_uri配置,必须是在开放平台中授权回调域配置下的地址,否则无法生成二维码信息,其次该地址必须可以外网访问到,否则会提示链接无效。
配置redirect_uri的过程中有两种思路,且都做了尝试,是没有问题的,但具体采用哪种就要结合具体的业务场景及所采用的技术框架了:
1、配置为具体的页面,授权登录成功后,原有登录页会重定向到这个页面,页面获取到url中的参数,带参数请求服务端,做后续的逻辑处理。
2、配置为后台的接口地址,授权成功后,服务端获取code,做后续逻辑处理(但会有个问题,前端要如何知道授权成功了?)。
我项目中开发目前使用的是第一种,具体实现思路如下:
因为是在登录页做微信扫码登录,所以我的重定向地址还是指向到登录页面,只不过在钩子函数mounted()中判断url中是否含code,存在即访问服务端做相应的逻辑处理,之后前端再做页面跳转。
mounted(){
const url = window.location.href;
console.log(url);
if(url.indexOf("code")!=-1){
console.log("用户已扫码,进行后端登录逻辑确认。")
let query = url.split("?")[1];
//js中获取get请求的参数。
let searchParams = new URLSearchParams(query);
//带code参数访问服务端接口。
this.$api.get("服务端接口",{code:searchParams.get("code")},res =>{
//用户登录成功的业务逻辑
this.wx_userinfo = res.data;
this.is_qr_show = false;
this.$route.push({name:"Test"})
});
}
}
根据服务端返回的状态值,页面做相应的逻辑处理。
注:mounted函数中获取当前的地址栏函数,需要用window.location.href来获取,一开始打算是使用路由守卫函数来监听,发现重定向进入页面后并不会触发监听。
服务端的接收方式和处理逻辑与方式一的一致,唯一不同的是在服务端进行页面重定向,并把用户id传到页面,页面中做相应的处理(比如:用户信息获取),以下是一个测试服务端重定向的示例:
@GetMapping("test_redir")
public String test(HttpServletResponse response) {
System.out.println(123);
return "redirect:http://localhost:8080/#/test2?id=12312";
}
事实证明,回调test_redir时,页面会重定向至指定的页面。
可能存在的问题?
采用的是前后端分离的模式,这样回调可能会存在传值的问题,若是以前使用springMVC的框架,则应该是用这种方式。
另外:网上看到还有一种实现方法是服务端不做页面重定向,而是在前端进行轮询判断用户是否登录成功。
使用方式一,由于使用的vue框架开发,vue路由默认使用的是hash模式,访问页面过程中url中会有#,此时配置到redirect_uri中时,#后面的内容会被清掉(微信自动处理),所以配置重定向地址时不能有#。
解决思路:
1、修改vue的路由模式为“history”,路径访问就不会有#。
2、将需要重定向的页面设置为根路由(一般情况下根路由就是登录页面),就可以在该页面做相应的逻辑处理。
由于微信开发的很多回调和通知都需要互联网可以访问到的服务,所以本地开发调试的时候就需要本地服务可以映射到互联网,即微信回调可以访问到的服务。
微信公众平台测试账号系统
微信公众平台接口调试工具
在账号申请或者审核阶段,就可以使用沙箱账号进行开发测试。
按照官方文档,也可以实现基础的功能,但是要和已有的业务系统结合,嵌入功能后依然流畅就需要动脑经怎么去优化了,自己整理一遍,对微信授权登录的过程会更清晰,若有对应场景的开发,可能还得依托官方文档来实现,但是思考问题的深度就不一样了。