SpringBoot实现扫码登录
文章目录
- 一、概述
- 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实现扫码登录相关推荐
- 基于SpringBoot+webSocket实现扫码登录功能
作者:93年颈椎病人 blog.csdn.net/q826qq1878/article/details/91041679 最近单位又有一个新Java项目. 涉及到扫码登录.之前项目使用的是 ajax轮 ...
- SpringBoot整合微信扫码登录
SpringBoot整合微信扫码登录 准备工作 基本思路流程 搭建SpringBoot 引入依赖 加入配置文件 代码实现 工具类 controller层 结果 准备工作 1.登录官网了解到,学习者想本 ...
- 基于 springBoot 实现webSocket方式的扫码登录
最近单位又有一个新Java项目. 涉及到扫码登录.之前项目使用的是 ajax轮询的方式.感觉太low了. 所以这次用webSocket的方式进行实现 好.废话不多说!咱们开始!! 一.首先咱们需要一张 ...
- java 系统生成二维码实现扫码登录 springboot 生成二维码
文章目录 前言 一.生成二维码 二.业务流程和代码逻辑梳理 总结 前言 使用框架 springboot 自己系统生成二维码,到前端网站,以及APP扫码登录流程,业务流程讲解梳理.也为自己做记录. ...
- SpringBoot+webSocket 实现扫码登录功能
最近单位又有一个新Java项目. 涉及到扫码登录.之前项目使用的是 ajax轮询的方式.感觉太low了. 所以这次用webSocket的方式进行实现 好.废话不多说!咱们开始!! 一.首先咱们需要一张 ...
- java mirai-2.15 qq机器人扫码登录或springboot项目进行扫码登录
请下载我的jar包在项目中引用 [java-mirai-qrcode-0.1.jar] jar包有参考 mirai-console-dev-qrlogin-0.1.7在java中整合 适应mirai版 ...
- SpringBoot+webSocket实现扫码登录功能
今日推荐强制双休!腾讯调整加班机制,21 点前必须离开工位 使用雪花id或uuid作为Mysql主键,被老板怼了一顿! 盘点 12 个 GitHub 上的高仿项目 CTO 说了,用错 @Autowir ...
- SpringBoot + Web Socket 实现扫码登录,这种方式太香了!!
最近单位又有一个新Java项目. 涉及到扫码登录.之前项目使用的是 ajax轮询的方式.感觉太low了. 所以这次用webSocket的方式进行实现 好.废话不多说!咱们开始!! 一.首先咱们需要一张 ...
- springboot实现微信扫码登录和绑定
前言:系统中若用到微信扫码登录,则要进行微信公众账号授权,所以在开发功能之前, 需要到微信公众平台申请一个服务号,当然仅仅只是作为开发者,则使用测试公众账号也行. 有了公众号后,则需登录公众后台进行一 ...
最新文章
- 2022-2028年中国阻燃母料行业市场深度分析及发展规模预测报告
- Linux下设置时区(通过shell设置和程序中设置)及程序中设置环境变量
- Orleans 高级特性-目录
- Python类的实例属性详解
- 西瓜书+实战+吴恩达机器学习(十七)规则学习(序贯覆盖)
- 会走索引吗 oracle_茅台酒会走兰花的老路吗?
- 面试者应向公司问什么问题?
- ICH E2B | ICSR 电子传输网关对接解决方案
- NLP分词使用总结--python HANLP
- [渝粤教育] 北京师范大学 中国哲学 参考 资料
- Java内存模型中happen-before原则
- mac os 配置hosts
- 逻辑思维能力选择题30道
- python语言命名规则-一文轻松掌握python语言命名规范规则
- android-studio安装过程详解
- DirectX 图形接口指南
- 大连医科大学中山学院计算机科学与技术,2021年大连医科大学中山学院各专业录取分数线...
- php验证码刷新_php验证码刷新与局部刷新
- 印度加速量子计算布局,推出国内首个量子计算机模拟器工具包
- 社会底层是怎样炼成的(牛叔)