扫码登录原理及实践

 

前言

近期实现了一个扫码登录的需求,在此之前没有这方面的开发经历,所以接到这个需求的时候还是有点慌的,最终通过查阅网上的资料以及老大的指导下实现了这个功能,目前已经投入使用,实现之后还是蛮兴奋的。特此记录一下实现的过程。

主要原理

怎么实现的呢?首先得了解其中的原理,由于我只提供后台接口,因此只记录后台接口的来龙去脉。先贴张图吧

图是我自己画的,主要原理其实不难,分为如下几个步骤:

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。

整体就是这样,前端的细节此处就不再详述了,有兴趣的同学可以再评论底下留言,相互探讨下也是十分乐意的

猜你喜欢

转载自blog.csdn.net/ccc1234_/article/details/81711241