通过微信扫码和微信交互,最终拿到openid(相当于数据库主键id,是微信用户唯一标识),然后通过openid和业务交互。
一共4个步骤,其实不论是微信授权登录,还是QQ授权登录,或者支付宝授权登录…等只要是OAuth2.0协议都是这逻辑
1 第一步:用户同意授权,获取code 2 第二步:通过code换取网页授权access_token 3 第三步:刷新access_token(如果需要) 4 第四步:拉取用户信息(需scope为 snsapi_userinfo)
https://open.weixin.qq.com
注册微信开发平台
准备营业执照,1-2个工作日审批,300元
提交审核,七个工作日审批
https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
获取access_token时序图:
以vue.js
为例:
需要在loginApi.js
中配置接口,检查是否登录。
checkLogin() {
return request({
url: `/user/wx/checklogin`,
method: 'get',
})
}
在login.vue
中添加属性
// 需要在页面head中先引入如下 JS 文件(支持https):
head: {
script: [
{src: 'http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'}
]
},
// 定义对话框开启关闭的boolean类型
data() {
return {
wxDialog: false
}
}
添加微信登录对话框
// @opened 对话框打开后的回调
<el-dialog
:visible.sync="wxDialog"
@opened="wxOpen"
width="30%",
center
>
<div id="qrCode" style="padding-left: 110px;"></div>
</el-dialog>
在微信图标添加点击事件
<a id="weixin" class="weixin" target="_blank" @click="wxDialog=true">
<i class="iconfont icon-weixin"/>
</a>
在methods
中添加弹出二维码方法,这里用到了之前申请的appid
,redirect_uri
等,注意了解下scope
methods: {
wxCreate() {
var obj = new WxLogin({
self_redirect:true,
id:"qrCode",
appid: "",
scope: "",
redirect_uri: "",
state: "",
style: "black",
href: ""
});
},
wxOpen() {
// 生成登录二维码
this.wxCreate();
// 验证是否已经扫码
loginApi.checkLogin().then(response => {
if (response.success) {
// 携带token跳转到到首页
this.$router.push( path:'/',query:{token:response.data.token}})
}else {
this.$message({
type: 'error',
message: '登录失败,请重试'
});
this.wxOpen();
}
})
}
}
前端的总体思路就是:点击微信icon打开对话框,此时不断检查是否登录,如果已经登录,跳转首页,登陆成功,未登录,等待扫码,扫码后回调
总体思路为:再死循环中不断检查isLogin标志位是否为TRUE
@RestController
@RequestMapping("/user")
public class WxLoginController {
final String app_id = "wx92b66fgfg8c01fc87";
final String app_secret = "d734ba63fgfggb3b573d7cb1cdcb958eea";
final String redirect_url = "http://wwwxxxx.com/wechat/callBack";
/**
* 记录当前用户是否已经登录
*/
private boolean isLogin;
/**
* 记录当前用户是否扫码登录失败
*/
private boolean isLoginFail;
/**
* 检查是否登录
* @return 是否登录
*/
@GetMapping("/wx/checklogin")
public ResponseEntity<Object> checkLogin() {
ResponseEntity<Object> responseEntity;
// 定义flag
int flag = 1;
// 一直查询是否登录
while (true) {
// 检查flag是否超过120 即60s
if (flag > 120) {
responseEntity = new ResponseEntity<>("登录超时",HttpStatus.INTERNAL_SERVER_ERROR);
break;
}
// 登陆成功
if (isLogin) {
// 如果已经登录,返回用户token
responseEntity = new ResponseEntity<>(token,HttpStatus.OK);
// 登录状态复位
isLogin = false;
break;
}
// 登录失败
if (isLoginFail) {
responseEntity = new ResponseEntity<>("登录失败",HttpStatus.INTERNAL_SERVER_ERROR);
break;
}
// 每隔0.5s查询一次状态
try {
Thread.sleep(500);
}catch (Exception e) {
e.printStackTrace();
}
flag++;
}
return responseEntity;
}
}
final String app_id = "wx92b66fgfg8c01fc87";
final String app_secret = "d734ba63fgfggb3b573d7cb1cdcb958eea";
final String redirect_url = "http://wwwxxxx.com/wechat/callBack";
@GetMapping("/wx/callback")
public ResponseEntity<Object> callback(String code, String state) {
if ("fm".equals(state)) {
try {
// 1.换取access_token
String getToken = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
"appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";
// 拼接请求地址
getToken = String.format(getToken,
app_id,
app_secret,
code);
// 2.发送请求,换取access_token
Map<String, Object> tokenMap = CommonUtils.doGet(getToken);
Object rsStr = tokenMap.get("rsStr");
Map resMap = (Map)rsStr;
// 3.解析返回值,拿到access_token 和 open_id
String accessToken = (String) resMap.get("access_token");
String openid = (String) resMap.get("openid");
JSONObject jsonObject = new JSONObject();
jsonObject.put("accessToken",accessToken);
jsonObject.put("openid",openid);
// TODO 根据openid去换id
// TODO 成功登录,返回token
// 修改标志
this.isLogin = true;
// ......这里就可以和我们具体业务对接
......
return new ResponseEntity<>(jsonObject,HttpStatus.OK);
}catch (Exception e) {
this.isLoginFail = true;
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}