前言
近期实现了一个扫码登录的需求,在此之前没有这方面的开发经历,所以接到这个需求的时候还是有点慌的,最终通过查阅网上的资料以及老大的指导下实现了这个功能,目前已经投入使用,实现之后还是蛮兴奋的。特此记录一下实现的过程。
主要原理
怎么实现的呢?首先得了解其中的原理,由于我只提供后台接口,因此只记录后台接口的来龙去脉。先贴张图吧
图是我自己画的,主要原理其实不难,分为如下几个步骤:
1、前端生成一张二维码,使用qrCode.js插件。
2、前端定时请求后台二维码状态,此时在第一次请求的时候可以记录下时间,作为二维码失效时间的对比,数据存储建议使用redis。
3、手机扫描二维码,此时要请求后台接口,将识别出来的二维码值以及登录信息传入后台,改变后台二维码的状态为【已扫描】。
4、服务器给前端返回【已扫描】标识。
5、手机扫描之后出现确认登录按钮,点击确认登录,请求后台,改变二维码的状态为【已确认】。
6、服务器判断二维码状态为【已确认】之后,便根据用户的登录信息返回用户信息,前端渲染个人主页,实现扫码登录。
实现细节
1、定义一个CodeBean实体类
public class CodeBean implements Serializable{
// 二维码唯一标识
private String qrCodeValue;
// 二维码状态
private Integer qrCodeStatus;
// 用户登录身份证
private String token;
// 二维码生成时间
private Long createTime;
// 员工工号
private String workerNo;
public String getWorkerNo() {
return workerNo;
}
public void setWorkerNo(String workerNo) {
this.workerNo = workerNo;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public String getQrCodeValue() {
return qrCodeValue;
}
public void setQrCodeValue(String qrCodeValue) {
this.qrCodeValue = qrCodeValue;
}
public Integer getQrCodeStatus() {
return qrCodeStatus;
}
public void setQrCodeStatus(Integer qrCodeStatus) {
this.qrCodeStatus = qrCodeStatus;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
2、提供给app端的接口
public RstUtil loginByCode(CommonRemoteInputBean bean, AppMainBean mainBean) {
// 参数判断,此处省略
JSONObject json = new JSONObject();
long endTime = 0;
long startTime = 0;
String qRCodeValue = bean.getQrCodeValue();
String appName = bean.getAppName();
// app扫码
if (bean.getLoginStep() == 1) {
endTime = System.currentTimeMillis();
// 判断是否失效
CodeBean codeBean = MobileRedisUtil.getCode(qRCodeValue);
if (codeBean != null && codeBean.getCreateTime() != null) {
startTime = codeBean.getCreateTime();
}
if (endTime - startTime >= SystemParamConstant.QRCODE_EXPIRETIME) {
// 二维码失效
MobileRedisUtil.removeCode(qRCodeValue);
return RstUtil.getRstUtil(false, RstUtil.ERROR, "二维码失效");
}
LoginUserBean userBean = MobileRedisUtil.getLoginUser(mainBean.getEmpNumber(), appName, bean.getToken());
if (null != userBean && bean.getToken().equals(userBean.getToken())) {
codeBean.setToken(bean.getToken());
// 设置二维码状态为已扫描
codeBean.setQrCodeStatus(SystemParamConstant.QRCODE_SCANNED);
codeBean.setWorkerNo(mainBean.getEmpNo());
MobileRedisUtil.saveCode(codeBean);
json.put("data", codeBean);
return RstUtil.getRstUtil(true, RstUtil.SUCCESS, "扫描成功!", json);
} else {
MobileRedisUtil.removeCode(qRCodeValue);
return RstUtil.getRstUtil(false, RstUtil.ERROR, "未获取到人员信息!");
}
}
// 2、app确认登录
if(bean.getLoginStep() == 2) {
LoginUserBean userBean = MobileRedisUtil.getLoginUser(mainBean.getEmpNumber(),appName, bean.getToken());
CodeBean codeBean = MobileRedisUtil.getCode(qRCodeValue);
if (null != userBean && bean.getToken().equals(userBean.getToken())) {
if (codeBean != null &&
SystemParamConstant.QRCODE_SCANNED.equals(codeBean.getQrCodeStatus())){
codeBean.setQrCodeStatus(SystemParamConstant.QRCODE_CONFIRMED);
json.put("data", codeBean);
MobileRedisUtil.saveCode(codeBean);
}
}
return RstUtil.getRstUtil(true, RstUtil.SUCCESS, null, json);
}
return RstUtil.getRstUtil(false, RstUtil.ERROR, "系统异常!");
}
3、前端请求的接口
/**
* 二维码扫码登陆
* @param bean
* @param mainBean
* @return
*/
private RstUtil qrCodeLogin(CommonRemoteInputBean bean,AppMainBean mainBean){
if(null == MobileRedisUtil.getCode(bean.getQrCodeValue())){
CodeBean codeBean = new CodeBean();
codeBean.setCreateTime(System.currentTimeMillis());
codeBean.setQrCodeValue(bean.getQrCodeValue());
codeBean.setQrCodeStatus(SystemParamConstant.QRCODE_CREATED);
MobileRedisUtil.saveCode(codeBean);
}
// 获取是否存在确认登陆信息
CodeBean codeBean = MobileRedisUtil.getCode(bean.getQrCodeValue());
// 如果用户已经点击确认登陆,直接登陆系统
if(SystemParamConstant.QRCODE_CONFIRMED.equals(codeBean.getQrCodeStatus())){
String appName = bean.getAppName();
if(StringUtil.isEmpty(appName)){
appName = mainBean.getAppName();
}
try {
UserBean user = getLoginUser(codeBean.getWorkerNo() ,null);
LoginUserBean userBean = MobileRedisUtil.getLoginUser(user.getWorkerId(), "jjr", codeBean.getToken());
// 保存登陆信息
if(userBean != null && codeBean.getToken().equals(userBean.getToken())) {
// 生成用户登陆token
user.setToken(userBean.getToken());
userBean.setAppName(appName);
userBean.setLoginTime(new Date());
userBean.setImei(mainBean.getImei());
userBean.setServiceCode(mainBean.getServiceCode());
MobileRedisUtil.saveLoginUser(userBean);
} else {
MobileRedisUtil.removeCode(codeBean.getQrCodeValue());
return RstUtil.getRstUtil(false, RstUtil.ERROR, "登录失败!");
}
// 删除二维码标识
MobileRedisUtil.removeCode(codeBean.getQrCodeValue());
JSONObject json = new JSONObject();
json.put("data", user);
return RstUtil.getRstUtil(true, RstUtil.SUCCESS, "登录成功!", json);
}catch (Exception e) {
MobileRedisUtil.removeCode(codeBean.getQrCodeValue());
e.printStackTrace();
return RstUtil.getRstUtil(false, RstUtil.ERROR, "登录失败!");
}
}
if(SystemParamConstant.QRCODE_SCANNED.equals(codeBean.getQrCodeStatus())) {
return RstUtil.getRstUtil(true, RstUtil.SUCCESS, "已扫描,请在手机端确认");
}
if(SystemParamConstant.QRCODE_CREATED.equals(codeBean.getQrCodeStatus())) {
return RstUtil.getRstUtil(true, RstUtil.SUCCESS, "");
}
return RstUtil.getRstUtil(false, RstUtil.ERROR, "暂未确认登录!");
}
几个值得注意的地方:
1、二维码失效或使用完必须删除在redis中存储的信息。
2、人员验证需要使用token。
整体就是这样,前端的细节此处就不再详述了,有兴趣的同学可以再评论底下留言,相互探讨下也是十分乐意的