承接上篇博客,本文旨在实现二维码生成的功能,并在redis中存储一个key方便后期APP端扫码时做映射
本文代码git https://github.com/xvshu/qrlogin
1,生成二维码请求action
主要是生成一个唯一标识的key,本次以时间戳为测试key,存入redis,并传回前台,生成相关页面以供APP扫描
package com.el.qr.login.web; import com.el.qr.login.service.RedisQRService; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import java.util.Date; /** * 首页控制器 * * @author xvshu */ @Controller @RequestMapping("/qr") public class QRLoginController { private static final Logger logger = LoggerFactory.getLogger(QRLoginController.class); @Autowired private RedisQRService redisQRService; @RequestMapping(value = "/login") public ModelAndView index() { ModelAndView mav = new ModelAndView("qrlogin/qrlogin"); String codeMark = "xvshu-qrlogin-"+String.valueOf(System.currentTimeMillis()); mav.addObject("code_mark",codeMark); redisQRService.initQRKey(codeMark); return mav; } @RequestMapping(value = "/login/success_check") public ModelAndView success_check(String code_mark) { ModelAndView mav = new ModelAndView("qrlogin/qrsuccess"); mav.addObject("code_mark",code_mark); return mav; } @RequestMapping(value = "/login/main") public ModelAndView main(String userId) { ModelAndView mav = new ModelAndView("qrlogin/qrmain"); mav.addObject("userId",userId); return mav; } @RequestMapping(value = "/login/success") @ResponseBody public String success(String code_mark,String userID) { String result = "fail"; if(StringUtils.isNotEmpty(code_mark)&&StringUtils.isNotEmpty(userID)){ redisQRService.setQRKey(code_mark,userID); logger.info("=/login/success=>code_mark:{} userID:{}",code_mark,userID); result="success"; } return result; } @RequestMapping(value = "/login/check") @ResponseBody public String check(String code_mark) { String result = "nouser"; String qr_value = redisQRService.getQRValue(code_mark); logger.info("=/login/check=>code_mark:{} qr_value:{}",code_mark,qr_value); if(StringUtils.isNotEmpty(qr_value)){ result=qr_value; } return result; } }
2,前台页面
主要应用qrcode.js生成二维码,二维码内容为验证url+唯一标识,方便跳转,并启动两个定时任务,其中一个3秒一次与后台通信,监听是否有用户扫描成功,如果成功则跳转登录成功页面,否则继续等待,第二个定时任务30秒执行一次,如果超过30还未登录成功,则自动暂停所有定时任务,提示用户二维码失效。
效果图:
代码:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"> <!DOCTYPE html> <html lang="en"> <head> <title>翼龙贷扫码登录</title> <script type="text/javascript" src="../../common/js/jquery-3.3.1.min.js" ></script> <script type="text/javascript" src="../../common/js/jquery.qrcode.min.js" ></script> <script type="text/javascript" > var int=self.setInterval("check_qrlogin()",2000); $(function() { var code_mark='${code_mark}'; $("#code_mark").val(code_mark); create_qrcode(); setTimeout(function(){alert("二维码失效,请刷新页面重新扫码!");clean_qrcode()},30000); }); function check_qrlogin(){ var code_mark='${code_mark}'; $.get("/qr/login/check?code_mark="+code_mark,function(data,status){ if(status=="success"&&data!='nouser'){ int=window.clearInterval(int); window.location.href="/qr/login/main?userId="+data; } }); } function create_qrcode(){ var code_mark=$("#code_mark").val(); if(code_mark==null || code_mark==""){ alert("随机码不能为空!"); return; } var baseUrl= "http://172.30.53.250:8080/qr/login/success_check?code_mark="; $("#code").text(""); $('#code').qrcode({ render : "canvas",//也可以替换为table width : 300, height : 300, text : baseUrl+ code_mark //二维码内置内容,如果时URL形式一般浏览器会自动加载 }); } function clean_qrcode(){ $("#code").html("<h1 style='color:red'>二维码失效,请刷新页面重新扫码!</h1>"); int=window.clearInterval(int); } </script> </head> <body style="margin: 0"> <div style="width: 100%;height: 30%;background-color:red;text-align: center"> <div style="width: 100%;height: 25%;background-color:red;text-align: center"></div> <h1 style="font-size: 35px;color: white;margin:auto;">扫码授权平台</h1> <p></p> <div style="color:white;text-align: center;">二维码30秒后失效,请尽快扫码登录</div> </div> <div style="width:100%;height:78%;text-align: center;background-color:white;text-align: center"> <div style="width: 100%;height: 5%;background-color:white;text-align: center"></div> <div style="width:100%;height:300px;text-align: center"> <p style="text-align: center;" class="code" id="code"> </p> </div> <p></p><p></p><p></p> <div style="width:100%;text-align: center"> 标识码:<input type="text" style="width:200px" id="code_mark" /> </div> </div> </body> </html>
二维码失效:
3,redis操作类
此类旨在存储app与后台服务的key-user对应关系,如果有用户扫描到并授权登录成功,则进行key-user对应。
package com.el.qr.login.service; import com.el.qr.login.utls.QRDateUtils; import com.el.qr.login.web.QRLoginController; import com.eloancn.common.utils.DateUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisOperations; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; @Service public class RedisQRService { private static final Logger logger = LoggerFactory.getLogger(QRLoginController.class); @Resource(name = "codisTemplate") private HashOperations hashOperations; @Resource(name = "codisTemplate") private RedisOperations redisOperations; private int DAY_LOSS=2; private String QR_KEY="qrlogin_"; public void initQRKey(String qrKey){ if(!hashOperations.hasKey(initKey(),initKey()+qrKey)){ hashOperations.put(initKey(),initKey()+qrKey,""); logger.info("=RedisQRService.setQRKey=>key:{} file:{} value:{}",initKey(),initKey()+qrKey,""); expire(initKey()); } } public void setQRKey(String qrKey,String value){ if(hashOperations.hasKey(initKey(),initKey()+qrKey)&& StringUtils.isEmpty(getQRValue(qrKey))){ hashOperations.put(initKey(),initKey()+qrKey,value); logger.info("=RedisQRService.setQRKey=>key:{} file:{} value:{}",initKey(),initKey()+qrKey,value); } } public String getQRValue(String qrKey){ return (String)hashOperations.get(initKey(),initKey()+qrKey); } private String initKey(){ return QR_KEY+QRDateUtils.getDataString(); } private boolean expire(String key){ return redisOperations.expire(key,DAY_LOSS, TimeUnit.DAYS); } }
总结:此部分较为容易,旨在对app进行最小的开发,完成二维码登录。