文章目录

  • 一、概述
    • 1、扫码登录介绍
    • 2、扫码登录原理
  • 二、扫码登录实战(轮询版)
    • 1、环境准备
    • 2、RedisTemplate序列化
    • 3、Token工具类
    • 4、定义扫码状态
    • 5、定义返回类
    • 6、定义二维码工具类
    • 7、编写相应方法
  • 三、扫码登录(长连接版)

一、概述

1、扫码登录介绍

二维码扫描登录原理

二维码登录本质上也是一种登录认证方式。既然是登录认证,要做的也就两件事情:告诉系统我是谁,以及向系统证明我是谁。

比如账号密码登录,账号就是告诉系统我是谁, 密码就是向系统证明我是谁; 比如手机验证码登录,手机号就是告诉系统我是谁,验证码就是向系统证明我是谁;

2、扫码登录原理

  • PC 端发送 “扫码登录” 请求,服务端生成二维码 id,并存储二维码的过期时间、状态等信息
  • PC 端获取二维码并显示
  • PC 端开始轮询检查二维码的状态,二维码最初为 "待扫描"状态
  • 手机端扫描二维码,获取二维码 id
  • 手机端向服务端发送 “扫码” 请求,请求中携带二维码 id、手机端 token 以及设备信息
  • 服务端验证手机端用户的合法性,验证通过后将二维码状态置为 “待确认”,并将用户信息与二维码关联在一起,之后为手机端生成一个一次性 token,该 token 用作确认登录的凭证
  • PC 端轮询时检测到二维码状态为 “待确认”
  • 手机端向服务端发送 “确认登录” 请求,请求中携带着二维码 id、一次性 token 以及设备信息
  • 服务端验证一次性 token,验证通过后将二维码状态置为 “已确认”,并为 PC 端生成 PC 端 token
  • PC 端轮询时检测到二维码状态为 “已确认”,并获取到了 PC 端 token,之后 PC 端不再轮询
  • PC 端通过 PC 端 token 访问服务端

二、扫码登录实战(轮询版)

1、环境准备

  • SpringBoot
  • Lombok
  • Redis

2、RedisTemplate序列化

//序列化RedisTemplate
@Configuration
public class RedisConfig {// 编写自己的RedisTemplate@Bean@SuppressWarnings("all")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);//序列化配置Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper om = new ObjectMapper();// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和publicom.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常// 序列化时会自动增加类类型,否则无法反序列化om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);//String的序列化StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);// hash采用String序列方式template.setHashKeySerializer(stringRedisSerializer);// value采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的value采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}
}

3、Token工具类

/*** token的工具类* 使用jwt生成/验证token(jwt JSON Web Token)* jwt由三部分组成: 头部(header).载荷(payload).签证(signature)* <p>* 1.header头部承载两部分信息:* {*   “type”: “JWT”, 声明类型,这里是jwt*   “alg”: “HS256” 声明加密的算法 通常直接使用 HMAC SHA256* }* 将头部进行base64加密, 构成了第一部分* <p>* 2.payload载荷就是存放有效信息的地方*  (1).标准中注册的声明*  (2).公共的声明 (一般不建议存放敏感信息)*  (3).私有的声明 (一般不建议存放敏感信息)* 将其进行base64加密,得到Jwt的第二部分* <p>* 3.signature签证信息由三部分组成:* (1).header (base64后的)* (2).payload (base64后的)* (3).secret* 需要base64加密后的header和base64加密后的payload连接组成的字符串,* 然后通过header中声明的加密方式进行加盐secret组合加密,构成了jwt的第三部分*/
@Slf4j
public class TokenUtil {/*** token的失效时间:25天*/private final static long TIME_OUT = 25 * 24 * 60 * 60 *1000L;/*** token的密钥*/private final static String SECRET = "shawn222";/*** 生成token** @return String*/public static String token(String userId) {String token = null;try {Date date = new Date(System.currentTimeMillis() + TIME_OUT);Algorithm algorithm = Algorithm.HMAC256(SECRET);Map<String, Object> headers = new HashMap<>();headers.put("type", "jwt");headers.put("alg", "HS256");token = JWT.create().withClaim("account", userId).withExpiresAt(date).withHeader(headers).sign(algorithm);} catch (IllegalArgumentException | JWTCreationException e) {e.printStackTrace();}return token;}/*** token验证** @param token token* @return String*/public static boolean verify(String token) {try {Algorithm algorithm = Algorithm.HMAC256(SECRET);JWTVerifier jwtVerifier = JWT.require(algorithm).build();DecodedJWT decodedJWT = jwtVerifier.verify(token);// 客户端可以解密 所以一般不建议存放敏感信息log.info("account:" + decodedJWT.getClaim("account").asString());return true;} catch (IllegalArgumentException | JWTVerificationException e) {e.printStackTrace();return false;}}

4、定义扫码状态

public enum CodeStatus {/*** 过期*/EXPIRE,/*** 未使用的二维码*/UNUSED,/*** 已扫码, 等待确认*/CONFIRMING,/*** 确认登录成功*/CONFIRMED}

5、定义返回类

@Data
@NoArgsConstructor
public class CodeVO<T> {/*** 二维码状态*/private CodeStatus codeStatus;/*** 提示消息*/private String message;/*** 正式 token*/private T token;public CodeVO(CodeStatus codeStatus) {this.codeStatus = codeStatus;}public CodeVO(CodeStatus codeStatus,String message) {this.codeStatus = codeStatus;this.message = message;}public CodeVO(CodeStatus codeStatus,String message,T token) {this.codeStatus = codeStatus;this.message = message;this.token=token;}}

6、定义二维码工具类

/*** 二维码工具类*/
public class CodeUtil {/*** 获取过期二维码存储信息** @return 二维码值对象*/public static CodeVO getExpireCodeInfo() {return new CodeVO(CodeStatus.EXPIRE,"二维码已更新");}/*** 获取未使用二维码存储信息** @return 二维码值对象*/public static CodeVO getUnusedCodeInfo() {return new CodeVO(CodeStatus.UNUSED,"二维码等待扫描");}/*** 获取已扫码二维码存储信息*/public static CodeVO getConfirmingCodeInfo() {return new CodeVO(CodeStatus.CONFIRMING,"二维码扫描成功,等待确认");}/*** 获取已扫码确认二维码存储信息* @return 二维码值对象*/public static CodeVO getConfirmedCodeInfo(String token) {return new CodeVO(CodeStatus.CONFIRMED, "二维码已确认",token);}}

7、编写相应方法

@Slf4j
@Service
public class LoginService {@ResourceRedisTemplate<String, Object> redisTemplate;/*** 生成uuid*/public CommonResult<String> generateUUID(){try{String uuid = UUID.randomUUID().toString();redisTemplate.opsForValue().set(RedisKeyUtil.getScanUUID(uuid),CodeUtil.getUnusedCodeInfo(),RedisKeyUtil.getTimeOut(), TimeUnit.MINUTES);return new CommonResult<>(uuid);}catch (Exception e){log.warn("redis二维码生成异常{}",e.getMessage());}return new CommonResult("二维码异常,请重新扫描",400);}/*** uuid状态信息*/public CommonResult<CodeVO> getInfoUUID(String uuid) {Object object = redisTemplate.opsForValue().get(RedisKeyUtil.getScanUUID(uuid));if(object==null){return new CommonResult("二维码不存在或者已过期",400);}return new CommonResult<>((CodeVO)object);}/*** 扫描登录,去确认二维码*/public CommonResult scanQrLogin(String uuid, String account) {try {Object o = redisTemplate.opsForValue().get(RedisKeyUtil.getScanUUID(uuid));if(null==o){return new CommonResult<>("二维码异常,请重新扫描",400);}CodeVO codeVO = (CodeVO) o;//获取状态CodeStatus codeStatus = codeVO.getCodeStatus();// 如果未使用if(codeStatus==CodeStatus.UNUSED){redisTemplate.opsForValue().set(RedisKeyUtil.getScanUUID(uuid),CodeUtil.getConfirmingCodeInfo(),RedisKeyUtil.getTimeOut(), TimeUnit.MINUTES);//你的逻辑return new CommonResult<>("请确认登录",200,null);}}catch (Exception e){log.warn("二维码异常{}",e.getMessage());return new CommonResult<>("内部错误",500);}return new CommonResult<>("二维码异常,请重新扫描",400);}/*** 确认登录,返回学生token以及对应信息* @param uuid* @param id 学生id* @return*/public CommonResult confirmQrLogin(String uuid, String id) {try{CodeVO codeVO = (CodeVO) redisTemplate.opsForValue().get(RedisKeyUtil.getScanUUID(uuid));if(null==codeVO){return new CommonResult<>("二维码已经失效,请重新扫描",400);}//获取状态CodeStatus codeStatus = codeVO.getCodeStatus();// 如果正在确认中,查询学生信息if(codeStatus==CodeStatus.CONFIRMING){//你的逻辑// 生成tokenString token = TokenUtil.token(studentLoginVO.getAccount());//redis二维码状态修改,PC可以获取到redisTemplate.opsForValue().set(RedisKeyUtil.getScanUUID(uuid),CodeUtil.getConfirmedCodeInfo(token),RedisKeyUtil.getTimeOut(), TimeUnit.MINUTES);return new CommonResult<>("登陆成功",200);}return new CommonResult<>("二维码异常,请重新扫描",400);}catch (Exception e){log.error("确认二维码异常{}",e);return new CommonResult<>("内部错误",500);}}
}

三、扫码登录(长连接版)

当然不仅仅包括短轮训,还有SSE(Server-Send Events,可以用WebFlux实现)以及WebSocket长连接实现,可以参考:Spring Boot + Web Socket 实现扫码登录


参考文章:

Java 语言实现简易版扫码登录

Java实现二维码扫描登录

SpringBoot实现扫码登录相关推荐

  1. 基于SpringBoot+webSocket实现扫码登录功能

    作者:93年颈椎病人 blog.csdn.net/q826qq1878/article/details/91041679 最近单位又有一个新Java项目. 涉及到扫码登录.之前项目使用的是 ajax轮 ...

  2. SpringBoot整合微信扫码登录

    SpringBoot整合微信扫码登录 准备工作 基本思路流程 搭建SpringBoot 引入依赖 加入配置文件 代码实现 工具类 controller层 结果 准备工作 1.登录官网了解到,学习者想本 ...

  3. 基于 springBoot 实现webSocket方式的扫码登录

    最近单位又有一个新Java项目. 涉及到扫码登录.之前项目使用的是 ajax轮询的方式.感觉太low了. 所以这次用webSocket的方式进行实现 好.废话不多说!咱们开始!! 一.首先咱们需要一张 ...

  4. java 系统生成二维码实现扫码登录 springboot 生成二维码

    文章目录 前言 一.生成二维码 二.业务流程和代码逻辑梳理 总结 前言 使用框架  springboot  自己系统生成二维码,到前端网站,以及APP扫码登录流程,业务流程讲解梳理.也为自己做记录. ...

  5. SpringBoot+webSocket 实现扫码登录功能

    最近单位又有一个新Java项目. 涉及到扫码登录.之前项目使用的是 ajax轮询的方式.感觉太low了. 所以这次用webSocket的方式进行实现 好.废话不多说!咱们开始!! 一.首先咱们需要一张 ...

  6. java mirai-2.15 qq机器人扫码登录或springboot项目进行扫码登录

    请下载我的jar包在项目中引用 [java-mirai-qrcode-0.1.jar] jar包有参考 mirai-console-dev-qrlogin-0.1.7在java中整合 适应mirai版 ...

  7. SpringBoot+webSocket实现扫码登录功能

    今日推荐强制双休!腾讯调整加班机制,21 点前必须离开工位 使用雪花id或uuid作为Mysql主键,被老板怼了一顿! 盘点 12 个 GitHub 上的高仿项目 CTO 说了,用错 @Autowir ...

  8. SpringBoot + Web Socket 实现扫码登录,这种方式太香了!!

    最近单位又有一个新Java项目. 涉及到扫码登录.之前项目使用的是 ajax轮询的方式.感觉太low了. 所以这次用webSocket的方式进行实现 好.废话不多说!咱们开始!! 一.首先咱们需要一张 ...

  9. springboot实现微信扫码登录和绑定

    前言:系统中若用到微信扫码登录,则要进行微信公众账号授权,所以在开发功能之前, 需要到微信公众平台申请一个服务号,当然仅仅只是作为开发者,则使用测试公众账号也行. 有了公众号后,则需登录公众后台进行一 ...

最新文章

  1. 2022-2028年中国阻燃母料行业市场深度分析及发展规模预测报告
  2. Linux下设置时区(通过shell设置和程序中设置)及程序中设置环境变量
  3. Orleans 高级特性-目录
  4. Python类的实例属性详解
  5. 西瓜书+实战+吴恩达机器学习(十七)规则学习(序贯覆盖)
  6. 会走索引吗 oracle_茅台酒会走兰花的老路吗?
  7. 面试者应向公司问什么问题?
  8. ICH E2B | ICSR 电子传输网关对接解决方案
  9. NLP分词使用总结--python HANLP
  10. [渝粤教育] 北京师范大学 中国哲学 参考 资料
  11. Java内存模型中happen-before原则
  12. mac os 配置hosts
  13. 逻辑思维能力选择题30道
  14. python语言命名规则-一文轻松掌握python语言命名规范规则
  15. android-studio安装过程详解
  16. DirectX 图形接口指南
  17. 大连医科大学中山学院计算机科学与技术,2021年大连医科大学中山学院各专业录取分数线...
  18. php验证码刷新_php验证码刷新与局部刷新
  19. 印度加速量子计算布局,推出国内首个量子计算机模拟器工具包
  20. 社会底层是怎样炼成的(牛叔)

热门文章

  1. 人大金仓数据库KCA/KCP认证题库
  2. 约瑟夫问题-输出最后的编号
  3. 计算机网络网络协议与网络结构
  4. 软件智能:aaas系统对AI的诠释-AI的可能的三个取向和必然的一个成果(演绎逻辑-必然的推理-的两个独立性:推论和定论)
  5. PHP strtotime()函数
  6. java跑p_javap使用
  7. Windows、Linux系统常用CMD命令大全
  8. 三五族异质结的自发极化、压电极化及2DEG
  9. 马氏距离例题详解(全网最详细)
  10. eclipse离线安装PyDev