目录
微信扫码登录介绍
微信扫码登录是指微信OAuth2.0授权登录让微信用户使用微信身份安全登录第三方应用或网站,在微信用户授权登录已接入微信OAuth2.0的第三方应用后,第三方可以获取到用户的接口调用凭证(access_token),通过access_token可以进行微信开放平台授权关系接口调用,从而可实现获取微信用户基本开放信息和帮助用户实现基础开放功能等。
开发步骤
- 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
- 通过code参数加上AppID和AppSecret等,通过API换取access_token;
- 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。
微信扫码登录示例
如进去下面的网站时
https://passport.yhd.com/wechat/login.do
需要微信进行授权登录
当我们进行扫码登录后,服务器就可以通过微信开放平台获取登录用户的信息,达到对第三网站更为安全,更为人性化的体验。
微信开放文档
地址链接:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
具体微信提供给开发者的文档非常详细,具体可以参照开发文档进行操作。
遇到的问题
- 使用微信开放平台必须注册一个微信开放平台账号
微信开放平台链接:https://open.weixin.qq.com/
注册需要提供很详细的资料
必须要提供已备案的域名网站,最后提交信息后会进行审核 - 如果需要使用微信开放平台,必须要进行开发者资质认证
这个是一个很麻烦的事情。
- 当认证完成之后,我们即可以创建网站应用
接着又是填写一大堆资料,最后进行审核(1-3天,很麻烦)
- 当创建网站应用成功后,我们就可以使用改应用的域名开开心心的根据开发文档进行开发了
由于开放平台的开发者资质认证的要求比较苛刻,个人开发者难以达到要求,所以具体实现我并没有实践过,但是我重点要分享的时下面使用第三方工具进行微信授权。
如果有条件,要从事微信开发,可以尽量申请开发者资质。申请后不仅网站上可以进行微信授权开发,还有一下项目都可以进行微信开发。
但还是想吐槽微信开发不友好,每次新建一个项目或者申请都需要进行审核,有很大的被打回的风险,也很耗费时间,跟客服沟通经常联系不到,所以个人开发者真的比较困难。
使用第三方工具实现网站微信扫码登录
开发前介绍
本次要介绍的实现微信扫码登录的第三方工具叫做:码上登录
官网地址:http://login.vicy.cn/
码上登录,是一个为各网站提供微信扫一扫登录能力的平台。
支持个人网站接入,无需企业认证, 使您的网站即刻拥有微信 [扫一扫] 登录能力!
在官网中有使用场景案例和功能介绍,可以自行查看
其中官网中最重要的一个东西:就是API文档啦
API文档:http://login.vicy.cn/apiWord.html
里面的介绍也比较详细,不过第一次使用也会有写困难,慢慢就熟悉了。
开发步骤
- 首先使用微信登录码上登录官网,注册账号
- 然后就可以直接创建应用了
填写回调url的时候必须填写已备案域名的回调地址(下面细说) - 接着打开API文档,试着请求接口请求地址:https://server01.vicy.cn/8lXdSX7FSMykbl9nFDWESdc6zfouSAEz/wxLogin/tempUserId?secretKey=xxxxxxxxxxxxxxxxxx(即我们创建应用时所生成的secretKey)
请求成功之后返回以下信息:
- 如果拿得到qrCodeReturnUrl说明登录成功,接着需要开发服务器进行接受码上登录服务器发出的请求,改请求时post请求,并且携带参数
特别注意:当我们写后台回调时,使用controller,并且创建RequestMapping(""),改请求的路径必须是你创建应用是填写的url
代码示例:
项目创建的示例:/** * @param response * @param map * @return 接收参数回调,是被回调的,第三方码上登录回调 */ @RequestMapping("/loginService") @ResponseBody public LoginResultVO loginService( HttpServletResponse response, @RequestParam Map<String, String> map) { System.out.println(map); userMap = map; LoginResultVO loginResultVO = new LoginResultVO(); //System.out.println("我被调用了"); loginResultVO.setErrcode(0); loginResultVO.setMessage("成功"); return loginResultVO; }
- 是很重要的一步:当开发服务器接收到码上登录服务器发送的请求拿到数据时,开发者服务器需要向“码上登录”服务器返回一个是否登录成功的状态码。
返回数据类型有两个字段,返回 0 和 “成功” 即可。
当完成以上5个步骤后,一次微信扫码授权登录的过程才真正完成,也就是开发者服务器真正成功拿到了登录用户的信息数据。
微信扫码登录获取微信用户信息Demo实现流程
实现效果
- 登录界面
- 当微信扫码后
-
手机端
-
PC端
- 登录成功后
-
后台拿到数据
-
前端显示信息
实现过程
-
由于使用码上登录必须使用公网url进行开发,所以先使用内网穿透工具,穿透本地项目到外网。
推荐使用:NATAPP—内网穿透:https://natapp.cn/
具体使用查看教程:https://natapp.cn/article -
创建项目编写代码
-
使用到的工具
- 二维码生成包
<script src="https://cdn.bootcss.com/jquery/3.3.0/jquery.min.js"></script> <script src="https://blog-static.cnblogs.com/files/lovling/jquery.qrcode.js"></script>
- 创建二维码示例
<div id="output"></div> <script $('#output').qrcode({ text: qrCodeReturnUrl, // 二维码的内容 render: "canvas", // 设置渲染方式 width: 360, // 设置宽度: 默认256 height: 360, // 设置高度: 默认256 background: "#ffffff", // 背景颜色 foreground: "#000000", // 前景颜色 // src: "xxx.png", //二维码中间的图片 }); </script>
- Ajax轮询请求
什么是轮询?
轮询(polling):客户端按规定时间定时向服务端发送ajax请求,服务器接到请求后马上返回响应信息并关闭连接。 Ajax轮询需要服务器有很快的处理速度与快速响应。
Ajax轮询原理:客户端是按照规定时间(这个时间由你设定,此处默认为1秒)像服务端发送请求,前一次请求完成后,无论有无结果返回,一秒之后下一次请求又会发出。这就叫做Ajax轮询。
实现Ajax轮询示例<script> $(function(){ var code,status; function getResult(){ var params = { code: code, operate: '什么操作TODO:', }; $.ajax({ type: 'POST', url: "请求地址TODO:", data: params, success: function(response) { console.log('成功啦'); //对成功数据的操作TODO: clearInterval(status); }, dataType: 'json', timeout: 30*1000,// 超时时间 // 超时意味着出错了 error: function (error) { console.log('失败啦'); } }); } }); //获取code。如果code存在则调用轮询来获取数据 if(code){ status = setInterval(getResult, 1000); } </script>
- 实现登录的原理
- 使用ajax调用码上登录获取登录二维码相关信息的接口:https://server01.vicy.cn/8lXdSX7FSMykbl9nFDWESdc6zfouSAEz/wxLogin/tempUserId?secretKey=xxxxxxxxxxxxxxxxxx(即我们创建应用时所生成的secretKey),拿到码上登录服务端为我们提供的qrCodeReturnUrl。
- 使用二维码生成工具,生成信息为qrCodeReturnUrl的二维码
- 后端编写接口,判断是否拿到了用户信息。
- 前端生成二维码之后进行Ajax轮询,发送请求询问后端是否拿到了用户的信息,如果没有拿到,说明没有人扫二维码,继续轮询,如果拿到了,则结束Ajax轮询,设置二维码过期,不许再使用。
- 结束轮询,跳转到编写的用户信息页面
实现源码
- 前端页面
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<script>
window.location.href = "/login"
</script>
</body>
</html>
- qrcode.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>二维码</title>
<script src="https://cdn.bootcss.com/jquery/3.3.0/jquery.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/lovling/jquery.qrcode.js"></script>
</head>
<style type="text/css">
body {
background: #095f88;
}
/* 登录字体 */
.text {
color: cornsilk;
text-align: center;
margin-top: 50px;
opacity: 0.7;
}
/* 二维码 */
#output {
margin: 50px auto;
width: 360px;
padding: 0;
opacity: 0.8;
}
</style>
<body>
<h1 class="text">请使用微信进行扫码登录</h1>
<div class="main">
<div id="output"></div>
</div>
<h2 style=" text-align: center; color: #cccccc" id="msg"></h2>
</body>
<script type="text/javascript">
$.ajax({
type: "get",
url: "https://server01.vicy.cn/8lXdSX7FSMykbl9nFDWESdc6zfouSAEz/wxLogin/tempUserId?secretKey=62320bcb05f4428ca8b13c462e104777",
data: "data",
async: false,
dataType: "json",
success: function (response) {
// console.log('response :>> ', response);
qrCodeReturnUrl = response.data.qrCodeReturnUrl;
}
});
console.log(qrCodeReturnUrl)
$('#output').qrcode({
text: qrCodeReturnUrl, // 二维码的内容
render: "canvas", // 设置渲染方式
width: 360, // 设置宽度: 默认256
height: 360, // 设置高度: 默认256
background: "#ffffff", // 背景颜色
foreground: "#000000", // 前景颜色
// src: "http://qkongtao.cn/wp-content/uploads/2020/02/logo1.fa9c5aaa.png", //二维码中间的图片
});
//ajax的轮询
var timer1 = setInterval(checkScan, 1000);//每隔1s执行一次checkScan
var timer2 = null;
function checkScan() {
$.ajax({
type: 'get',
url: "/loginData",
// async: false,
success: function (data) {
console.log(data)
if (!$.isEmptyObject(data)) {//有用户扫描了该二维码,应停止对checkScanServlet的轮询
window.clearInterval(timer1);
console.log("拿到数据")
$('#msg').html(data.nickname + "已经扫描,请在客户端确认");//将二维码取消,防止其他人再扫
timer2 = setInterval(checkConfirm, 1000);//轮询用户是否确认登录
}
}
});
}
function checkConfirm() {
$.ajax({
type: 'get',
url: "/loginSuccess",
// async: false,
success: function () {
console.log("跳转********")
window.clearInterval(timer2);
window.location.href = "/loginSuccess"
}
});
}
</script>
</html>
- userInfo.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登陆信息</title>
</head>
<body>
<h1 style="text-align: center;">您的登录信息</h1>
<div style="width: 800px;height: 600px;margin: 50px auto;">
<h3>用户名:
<span th:text="${session.loginUser.nickname}"></span>
</h3>
<h3>openid:
<span th:text="${session.loginUser.userId}"></span>
</h3>
<h3>头像图片:
<img th:src="${session.loginUser.avatar}">
</h3>
<h3>登录ip地址:
<span th:text="${session.loginUser.ipAddr}"></span>
</h3>
</div>
</body>
</html>
- 后端实现
- LoginResultVO.java(返回数据的封装类)
package cn.kt.wxlogin.domain;
/**
* @author tao
* @date 2021-03-06 23:18
* 概要:
*/
public class LoginResultVO {
Integer errcode;
String message;
public Integer getErrcode() {
return errcode;
}
public void setErrcode(Integer errcode) {
this.errcode = errcode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "LoginResultVO{" +
"errcode=" + errcode +
", message='" + message + '\'' +
'}';
}
}
- WXLoginController.java
package cn.kt.wxlogin.controller;
import cn.kt.wxlogin.domain.LoginResultVO;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @author tao
* @date 2021-03-06 23:09
* 概要:
*/
@Controller
public class WXLoginController {
/*扫码用户信息*/
private Map<String, String> userMap = new HashMap<>();
/**
* @param response
* @param map
* @return 接收参数回调,是被回调的,第三方码上登录回调
*/
@RequestMapping("/loginService")
@ResponseBody
public LoginResultVO loginService(
HttpServletResponse response,
@RequestParam Map<String, String> map) {
System.out.println(map);
userMap = map;
LoginResultVO loginResultVO = new LoginResultVO();
//System.out.println("我被调用了");
loginResultVO.setErrcode(0);
loginResultVO.setMessage("成功");
return loginResultVO;
}
/**
* @return 负责跳转登录
*/
@RequestMapping("/login")
public String login() {
return "qrcode";
}
/**
* @author :tao
* @date :Created in 2021-03-07 1:24
* @param: :
* @return: 返回前端用户信息,判断二维码有没有被扫
*/
@RequestMapping("/loginData")
@ResponseBody
public Map<String, String> loginData() {
return userMap;
}
/**
* @author :tao
* @date :Created in 2021-03-07 1:30
* @return: map
* 登陆成功,进行跳转
*/
@RequestMapping("/loginSuccess")
public ModelAndView loginSuccess(HttpServletResponse response,
Map<String, String> map,
HttpServletRequest request) {
HttpSession session = request.getSession();
/*如果session中user有值,则不需要重复*/
if (request.getSession().getAttribute("loginUser") == null) {
session.setAttribute("loginUser", userMap);
}
//清空用户数据
System.out.println(userMap);
userMap = null;
return new ModelAndView("userInfo");
}
}
源码下载
链接:https://pan.baidu.com/s/1qlNECaOmMtWsOCtd_gMSqg
提取码:a9lh
复制这段内容后打开百度网盘手机App,操作更方便哦
把自己毕业设计中用到的小技术写成博客笔记,也花了很长时间整理,如果觉得对大家有帮助,可以点赞、评论、关注,长的帅的可以大方的打赏支持一下 ^ _ ^
有什么问题都可以联系我(评论就可以了!)