微信网页登陆

本篇博客简单总结一下我在实际项目中使用微信网页自定义登陆的解决方案。

一.需求:

​ 有一个h5页面,用户从手机微信客户端进入该网页,用户点击授权后进行登陆,如果该用户绑定过学号,则直接进入页面,如果没有绑定学号则跳转到学号绑定页面。

二.流程分析:

首先查阅微信公众号文档,网页授权登陆的流程如下

  1. 用户同意授权,获取code
  2. 通过code换取网页授权access_token
  3. 刷新access_token(如果需要)
  4. 拉取用户信息(需scope为 snsapi_userinfo)

在项目中,需要取得用户的openid,再查询自己的数据库拉取用户信息,因此需要两步,前端授权后重定向获得code,将code发送到后台,后台换取openid。如果自己的数据库中无法查询到用户信息,则进行上面的第四步,拉取用户信息。

image-20210618145713104

三.实现过程

第一步:前台获取code

官方文档

在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
getCode () {
this.code = ''
var local = window.location.href
this.code = this.getUrlCode().code

//用户第一次进入页面,调用getUrlCode方法,截取地址中的code失败,就会跳转到此链接,用户授权后就会返回一个带code的地址。
if (this.code == null || this.code == '') {
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='+ appid + '&redirect_uri=' + encodeURIComponent(local) + '&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect'
}
}

getUrlCode () { // 截取url中的code方法
var url = location.search
var theRequest = new Object()
if (url.indexOf('?') != -1) {
var str = url.substr(1)
var strs = str.split('&')
for (var i = 0; i < strs.length; i++) {
theRequest[strs[i].split('=')[0]] = (strs[i].split('=')[1])
}
}
return theRequest
},
第二步:code发送给后台处理

后台使用java的springboot搭建

持久层框架使用springboot data jpa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@GetMapping("wxapi/getUserInfo")
@ResponseBody
public ResultModel getUserInfo(@RequestParam("code") String code) throws Exception {

//第一步,设置code,appid和appsecret
String urlOne = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid +
"&secret=" + appsecret +
"&code=" + code +
"&grant_type=authorization_code";

RestTemplate restTemplate=new RestTemplate();
//解决乱码问题
restTemplate.getMessageConverters().set(1,new StringHttpMessageConverter(Charset.forName("UTF-8")));
//设置请求头
HttpHeaders headers = new HttpHeaders();
//设置请求类型为application/x-www-form-urlencoded
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> formEntity = new HttpEntity<String>(null,headers);
//第二步,得到openid和access_token
String body= restTemplate.exchange(urlOne , HttpMethod.GET, formEntity, String.class).getBody();
JSONObject jsonObject = JSONObject.parseObject(body);

String openid = jsonObject.getString("openid");
String access_token = jsonObject.getString("access_token");

//通过openid查询数据库判断是否有此人
Wxuser wxuser = wxuserRepository.findByOpenId(openid);
//有,返回信息,签发token
if(wxuser!=null){
JSONObject wxuserInfo = new JSONObject();
//返回所需用户信息
wxuserInfo.put("avatar",wxuser.getAvatar());
ResultModel resultModel = new ResultModel(20000,"ok",wxuserInfo);
return resultModel;
}
//没有,返回代码20001跳转到学号绑定页面,签发token
else{
//微信网页登陆第三步,拉取用户信息,获取用户头像,昵称等
String urlTwo = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token +
"&openid=" + openid +
"&lang=zh_CN";

String userInfo= restTemplate.exchange(urlTwo , HttpMethod.GET, formEntity, String.class).getBody();

JSONObject userInfoJson = JSONObject.parseObject(userInfo);
ResultModel resultModel = new ResultModel(20001,"ok",userInfoJson);
return resultModel;
}

}
第三步:前端处理

假设用户已经登陆成功,后台返回token和用户信息。

使用vuex将token和用户信息存入全局状态。

同时对请求进行拦截,在请求头中加入token,后台方面也需要加一个请求拦截器,对每一个请求的token进行验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
service.interceptors.request.use(
config => {
//vuex获取token
if (store.getters.token) {
config.headers['X-Token'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)

四.备用地址

微信的接口地址有多个,曾经我在一个项目中,有遇到过接口地址无法请求的问题,排查发现是微信接口出现了故障。因此建议后台微信api使用多个地址,当一条线路无法访问时自动接入其他线路。

开发者可以根据自己的服务器部署情况,选择最佳的接入域名(延时更低,稳定性更高)。除此之外,可以将其他接入域名用作容灾用途,当网络链路发生故障时,可以考虑选择备用域名来接入。请开发者使用域名进行API接口请求,不要使用IP作为访问。若有需要开通网络策略,开发者可以从获取微信服务器IP地址定期获取最新的IP信息。

  1. 通用域名(api.weixin.qq.com),使用该域名将访问官方指定就近的接入点;
  2. 通用异地容灾域名(api2.weixin.qq.com),当上述域名不可访问时可改访问此域名;
  3. 上海域名(sh.api.weixin.qq.com),使用该域名将访问上海的接入点;
  4. 深圳域名(sz.api.weixin.qq.com),使用该域名将访问深圳的接入点;
  5. 香港域名(hk.api.weixin.qq.com),使用该域名将访问香港的接入点。