使用工具:redis+qrcode.js+jquery.js
注意事项:扫码手机必须跟项目使用同一网络ip;不然网络ip不同扫码请求访问不到项目服务,
我这里是用手机连接电脑热点进行扫码;亲测!!!
整体实现描述:
前端生成二维码后,同时发起频繁请求来判断二维码的登入状态,用户扫码进行登入后,后端验证登入通过后,把二维码登入状态存入redis,前端接收到成功登入返回值后进行页面跳转。
1.前端登入页面生成二维码
扫码页面定义一个div容器用来存放生成的二维码,当点击扫码登入的时候使用单机事件,触发一个生成二维码的函数,函数执行qrcode.js的new QRCode(“放二维码的容器”,”二维码内容”)方法来生成一个二维码,二维码内容写入需要访问登入页面的url。同时使用js的setInterval(“执行的方法或代码”,”每隔多少毫秒执行”)方法生成一个定时器,开启定时器用来判断二维码的登入状态,因为考虑到二维码的唯一性,所以需要给二维码设置一个唯一标识,我们可以使用一个Math.random()的随机数方法加上一个当前时间的时间戳,组合成一个字符串,代表二维码的唯一标识,用来保证每个二维码标识的唯一性,把二维码的唯一标识拼接到二维码登入页面的url中,让扫码后登入页面的登入请求使用。
2.前端扫码后登入页面的登入请求
当我们扫码页面生成二维码之后我们就可以进行扫码登入了,扫描二维码进入到登入页面,因为二维码的唯一性,所以我们要携带着二维码的唯一标识来进行登入请求。因为我们把二维码的唯一标识已经放入二维码的访问url中了,所以我们可以直接获取二维码唯一标识,这里使用window对象的localtion.search的方法来获取请求路径中从问号(?)开始的url路径,也就是我们请求中拼接的二维码唯一标识,截取后获取二维码的唯一标识,发送一个登入请求携带着我们的二维码唯一标识,让后端用来存储二维码的登入状态。
3.后端二维码登入请求的处理
我们后端API接口收到带着二维码标识的登入请求后,就可以进行登入的业务处理了,首先需要验证账号密码是否正确,如果有误直接返回账号或密码验证失败。当账号密码都验证通过后我们对二维码进行处理,我们可以使用redis来存储一个token,用来验证前端登入请求是否登入成功,同时也是用户登入后的token凭证,Key就是二维码的唯一标识,因为是唯一的标识,所以不会造成不同的二维码登入冲突。存储token后就可以返回登入成功的请求了,来告诉扫码页面已经扫码登入成功了。
4.定时器获取后端二维码的登入状态
我们第一步生成二维码的时候,同时也开启了一个定时器,我们要使用定时器来获取二维码的登入状态,使用定时器调用一个函数,不断发送一个查看二维码登入状态的请求,设置好定时器时间每隔多少毫秒发送请求,因为我们生成的二维码是唯一性的,所以判断二维码登入状态,需要拿着二维码的唯一标识来进行后端的判断,后端收到定时器频繁发来的请求后,进行判断用户是否完成扫码登入,具体实现就是拿着二维码的唯一标识,查询redis是否存有成功登入后存储的token,如果有token证明扫码页面用户已经登入成功了,就可以返回登入成功状态与token凭证到前端了,让前端做出登入成功后的响应与处理。
5.前端登入成功后的响应与处理
前端定时器执行判断登入状态的请求,一旦接收到后端返回成功登入装态的返回值,就可以进行成功登入的响应处理了,首先获取成功登入的token凭证,使用SessionStore.setItem(“键”,”值”)方法把凭证存储到sessionStore中,让登入后的其他请求使用,使用 定时器.close()方法把判断登入状态的定时器关闭,最后进行登入成功后的页面跳转即可。完成扫码登入!!!
实现代码:
1.前端生成二维码并开启定时器:
//生成二维码函数
function getQRCode() {
//随机数+时间戳生成一个二维码唯一标识
var qrCode = Math.random().toString().substring(2) + new Date().getTime();
//生成二维码,第一个参数是放二维码的容器,第二个参数是二维码访问地址
new QRCode(document.getElementById("QRCodeDiv"), "http://192.168.1.113:2222/tm-shop-show/shop-front-end/QRCode.html?QRCode=" + qrCode);
//创建一个定时器执行函数获取二维码登入状态
var interval = setInterval(getQRCodeStatus, 1000);
//定时器函数获取二维码登入状态
function getQRCodeStatus() {
$.ajax({
url: "http://localhost:7777/user/user/getQRCodeStatus",
type: "post",
data: {"qrCode": qrCode},
dataType: "json",
success: function (result) {
if (result.code === 200) {
//登入成功把token存入sessionStore中
sessionStorage.setItem("token", result.result);
//跳转页面
$(location).attr("href", "index.html");
//关闭定时器
clearInterval(interval);
}
},
error: function () {
alert("获取登入状态失败");
}
})
}
}
2.扫码后登入页面的登入请求:
//登入函数
function userLogin() {
var search = window.location.search; //获取请求路径?后字符串
var QRCode = search.substring(8) //截取二维码唯一标识
var inputName = $("#inputName").val()
var inputPassword = $("#inputPassword").val()
//发送登入请求
$.ajax({
url: "http://192.168.1.113:7777/user/user/userLogin",
type: "post",
data: {"inputName": inputName, "inputPassword": inputPassword,"QRCode":QRCode},
dataType: "json",
success: function (result) {
if(result.code == 200){
alert("登入成功")
//登入成功后关闭页面
window.close();
}
},
error: function () {
alert("登入失败")
}
})
}
3.后端验证登入状态:
/**
* @title user
* @author lixiang
* @updateTime 2021/11/17 15:14
* @description :用户登入
*/
@Override
public RestReturnModel userLogin(LoginUserEntity loginUserEntity) {
//获取用户账号密码
UserEntity user = userMapper.getUser(loginUserEntity.getInputName());
if (null == user) {
//如果账号不存在返回账号不存在
return RestReturnModel.LOGIN_ERROR("账户不存在");
}
if (MD5Util.valid(loginUserEntity.getInputPassword(), MD5Util.MD5Upper("password", user.getPassword()))
) {
//如果密码不正确返回密码不正确
return RestReturnModel.LOGIN_ERROR("密码错误");
}
//生成token凭证
String token = JwtTokenUtil.generateToken(user);
if (null != loginUserEntity.getQRCode()) {
//如果是二维码登入存入的key为二维码唯一标识
redisTemplate.opsForValue().set(loginUserEntity.getQRCode(), token, 30, TimeUnit.MINUTES);
//返回登入成功
return RestReturnModel.LOGIN_SUCCESS(token);
}
//非二维码登入把token存入到redis
redisTemplate.opsForValue().set("token_" + user.getUsername(), token, 30, TimeUnit.MINUTES);
//返回登入成功
return RestReturnModel.LOGIN_SUCCESS(token);
}
4.后端接收定时器请求返回登入状态:
/**
* @title user
* @author lixiang
* @updateTime 2021/11/19 21:02
* @description :返回二维码登入状态
*/
@Override
public RestReturnModel getQRCodeStatus(String qrCode) {
//拿着二维码唯一标识获取redis中二维码状态
String token = (String) redisTemplate.opsForValue().get(qrCode);
//如果不为空则代表二维码登入成功了
if(null != token){
//返回成功登入状态
return RestReturnModel.LOGIN_SUCCESS(token);
}
//为空则代表二维码还未登入成功
return RestReturnModel.LOGIN_ERROR("暂未登入");
}