快速接入Google两步认证Google Authenticator

(一)介绍

 

 既然来看该文章就应该知道Google的两步认证是干什么的,这边再提供一次app的下载链接

(apkpure搜索谷歌身份验证器)

 验证原理讲解:

  1. 在数据库中查找该登陆用户之前绑定的32位随机码(该码一般会存入数据库)
  2. 调用API传入32位随机码,生成正确的6位验证码(每隔1min会变化)
  3. 根据用户输入的6位验证码和正确的6位验证码做匹配,相同则登陆成功,不同则验证码时间失效或错误

用户绑定讲解:

  1. 调用API生成32位随机码,准备绑定给用户
  2. 调用API生成二维码QR字符串,需要传入用户信息(比如邮箱,id,昵称等),标题,以及生成的32位随机码
  3. 调用API将二维码QR字符串转化为图片后以Base64的方式展现到前端页面上
  4. 用户使用app扫码添加后,在前端页面点击确认绑定,输入本次看到的6位验证码
  5. 后端根据本次获得的32位随机码,用户信息(用来确定数据库中用户记录),以及输入6位验证码,通过API传入32位随机码获得正确的6位验证码,当其与输入的验证码相同时,则绑定成功,把32位随机码持久化到数据库中对应用户记录上

(二)准备工作

 导入一下Maven依赖

         <!--google两步认证相关--><dependency><groupId>de.taimos</groupId><artifactId>totp</artifactId><version>1.0</version></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.10</version></dependency><dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.2.1</version></dependency>

 导入工具类GoogleAuthenticationTool

import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import de.taimos.totp.TOTP;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Hex;
import sun.misc.BASE64Encoder;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.security.SecureRandom;/*** @Author bilibili-nanoda* @Date 2021/8/13 10:33* @Version 1.0*/
public class GoogleAuthenticationTool {public static String generateSecretKey() {SecureRandom random = new SecureRandom();byte[] bytes = new byte[20];random.nextBytes(bytes);Base32 base32 = new Base32();return base32.encodeToString(bytes);}/*** 根据32位随机码获得正确的6位数字** @param secretKey* @return*/public static String getTOTPCode(String secretKey) {Base32 base32 = new Base32();byte[] bytes = base32.decode(secretKey);String hexKey = Hex.encodeHexString(bytes);return TOTP.getOTP(hexKey);}/*** 生成绑定二维码(字符串)** @param account   账户信息(展示在Google Authenticator App中的)* @param secretKey 密钥* @param title     标题 (展示在Google Authenticator App中的)* @return*/public static String spawnScanQRString(String account, String secretKey, String title) {try {return "otpauth://totp/"+ URLEncoder.encode(title + ":" + account, "UTF-8").replace("+", "%20")+ "?secret=" + URLEncoder.encode(secretKey, "UTF-8").replace("+", "%20")+ "&issuer=" + URLEncoder.encode(title, "UTF-8").replace("+", "%20");} catch (UnsupportedEncodingException e) {throw new IllegalStateException(e);}}/*** 生成二维码(文件)【返回图片的base64,若指定输出路径则同步输出到文件中】** @param barCodeData 二维码字符串信息* @param outPath     输出地址* @param height* @param width* @throws WriterException* @throws IOException*/public static String createQRCode(String barCodeData, String outPath, int height, int width)throws WriterException, IOException {BitMatrix matrix = new MultiFormatWriter().encode(barCodeData, BarcodeFormat.QR_CODE,width, height);BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(matrix);ByteArrayOutputStream bof = new ByteArrayOutputStream();ImageIO.write(bufferedImage, "png", bof);String base64 = imageToBase64(bof.toByteArray());if(outPath!=null&&!outPath.equals("")) {try (FileOutputStream out = new FileOutputStream(outPath)) {MatrixToImageWriter.writeToStream(matrix, "png", out);}}return base64;}/*** 将图片文件转换成base64字符串,参数为该图片的路径** @param dataBytes* @return java.lang.String*/private static String imageToBase64(byte[] dataBytes) {// 对字节数组Base64编码BASE64Encoder encoder = new BASE64Encoder();if (dataBytes != null) {return "data:image/jpeg;base64," + encoder.encode(dataBytes);// 返回Base64编码过的字节数组字符串}return null;}}

(三)使用流程

Tips:其实看工具类就已经知道怎么使用了,但我这边还是贴出我的代码以供参考

  • 首次绑定逻辑判断

UserController的login中判断该登陆用户是否存在32位随机码

    //登陆逻辑@PostMapping("/login")public String login(WebLoginDTO webLoginDTO, HttpSession httpSession, Model model, HttpServletRequest httpServletRequest,RedirectAttributes redirectAttributes) {System.out.println("尝试登录:" + webLoginDTO.getEmail() + ":" + webLoginDTO.getEmail());Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(webLoginDTO.getEmail(), webLoginDTO.getPassword());try {subject.login(token);} catch (IncorrectCredentialsException e) {e.printStackTrace();model.addAttribute("msg", "密码错误");return "error/systemError";} catch (AuthenticationException e) {e.printStackTrace();model.addAttribute("msg", "账户不存在");return "error/systemError";}//说明登录成功ActiveUser activeUser = (ActiveUser) subject.getPrincipal();if (activeUser.isLokced()) {model.addAttribute("msg", "账户被封锁");return "error/systemError";}//没有32位随机码的情况if(activeUser.getTwoFactorCode()==null||activeUser.getTwoFactorCode().equals(""))            {//前往code绑定页面redirectAttributes.addAttribute("userId",activeUser.getUser_id());//todo 处理设计该页面绑定谷歌认证码(QR二维码)return "redirect:/user/bindingGoogleTwoFactorValidate";}

  不存在则定向到 绑定页面(要携带用户信息,如id)

    /*** 前往谷歌两步验证绑定页面* @param userId* @return*/@GetMapping("/bindingGoogleTwoFactorValidate")public String toBindingGoogleTwoFactorValidate(@RequestParam("userId")int userId,Model model){String randomSecretKey = GoogleAuthenticationTool.generateSecretKey();User user = userService.getUserByUserId(userId);//此步设置的参数就是App扫码后展示出来的参数String qrCodeString = GoogleAuthenticationTool.spawnScanQRString(user.getEmail(),randomSecretKey,"pilipili2333");String qrCodeImageBase64 = null;try {qrCodeImageBase64 = GoogleAuthenticationTool.createQRCode(qrCodeString,null,512,512);} catch (WriterException | IOException e) {e.printStackTrace();}model.addAttribute("randomSecretKey",randomSecretKey);model.addAttribute("qrCodeImageBase64",qrCodeImageBase64);return "bindingGoogleTwoFactorValidate";}

前端页面发起ajax执行绑定,且输入本次的6位验证码做校验

function confirmBinding() {var googleRegex =/\d{6}/;var inputGoogleCode = window.prompt("请输入6位google验证码");if(googleRegex.test(inputGoogleCode)){$.ajax({url:"[[@{/user/bindingGoogleTwoFactorValidate}]]",type:"post",data:{"userId":"[[${param.userId}]]","randomSecretKey":"[[${randomSecretKey}]]","inputGoogleCode":inputGoogleCode},dataType:"json",success:function (data) {if(data.state==='success'){window.alert("绑定成功");}else if(data.state==='fail'){window.alert("操作失败:"+data.msg);}}});}else {window.alert("请正确输入6位google验证码")}}

 后端对执行绑定再做一次6位验证码是否正确的校验

     /*** 执行谷歌两步验证绑定* @return*/@PostMapping("/bindingGoogleTwoFactorValidate")@ResponseBodypublic String bindingGoogleTwoFactorValidate(@RequestParam("userId")int userId,@RequestParam("randomSecretKey")String randomSecretKey,@RequestParam("inputGoogleCode")String inputGoogleCode){JSONObject respJsonObj =new JSONObject();User user = userService.getUserByUserId(userId);if(user.getTwoFactorCode()!=null&&!user.getTwoFactorCode().equals("")){respJsonObj.put("state","fail");respJsonObj.put("msg","该用户已经绑定了,不可重复绑定,若不慎删除令牌,请联系管理员重置");return respJsonObj.toString();}String rightCode =GoogleAuthenticationTool.getTOTPCode(randomSecretKey);if(!rightCode.equals(inputGoogleCode)){respJsonObj.put("state","fail");respJsonObj.put("msg","验证码失效或错误,请重试");return respJsonObj.toString();}user.setTwoFactorCode(randomSecretKey);int res = userService.updateUserByUser(user);if(res>0){respJsonObj.put("state","success");}else {respJsonObj.put("state","fail");respJsonObj.put("msg","数据库操作失败");}return respJsonObj.toString();}
  • 登陆时校验6位验证码的逻辑

UserController的login方法中处理

@PostMapping("/login")public String login(WebLoginDTO webLoginDTO, HttpSession httpSession, Model model, HttpServletRequest httpServletRequest,RedirectAttributes redirectAttributes) {System.out.println("尝试登录:" + webLoginDTO.getEmail() + ":" + webLoginDTO.getEmail());/*shiro认证相关代码。。。*///注意:1min内有效String rightGoogleCode = GoogleAuthenticationTool.getTOTPCode(activeUser.getTwoFactorCode());if(!webLoginDTO.getGoogleCode().equals(rightGoogleCode)){model.addAttribute("msg","谷歌验证码不正确或已超时");return "error/systemError";}/*后续逻辑*/}

需要注意:

与短信验证,邮件验证不同,验证码的生成与刷新是由我们自己控制的,而对于这种谷歌两步认证,他是1min刷新一次,对于同时刻,我们事先约定好了一套加密解密规则。因此在进行输入的6位验证码验证时,应当在输入之后再去获得此刻正确的6位CODE,而不是事先生成好正确的Code,再等用户输入。后者可能会因为延时问题(用户动作很摸,app上的已经更新了,但系统保留的还是上一次),导致经常性的验证码失效

更多教程,可见我的官方网站:最咔酷线上教程:www.zuikakuedu.cn

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/39007.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

身份验证器 Authenticator 插件

1.插件支持浏览器 ChromeFirefoxEdge 2.关键特性 通过扫描二维码添加账号使用密码加密数据在浏览器之间同步数据备份数据到云服务或导出数据到文件智能过滤和搜索 官网地址: Authenticator Extension 官方说明文档&#xff1a;What is Authenticator Extension? | Authen…

前后端身份验证

1、web 开发模式 【】基于服务端渲染的传统 Web 开发模式 【】基于前后端分离的新型 Web 开发模式&#xff1a;依赖于 Ajax 技术的广泛应用。后端只负责提供 API 接口&#xff0c;前端使用 Ajax 调用接口的开发模式 2、身份认证 【】服务端渲染推荐使用 Session 认证机制 【】…

生信工作流框架搭建 | 04-nextflow与Slurm高性能计算

目录 生信工作流框架搭建 | 04-nextflow与Slurm高性能计算前情提要什么是HPC高性能计算什么是slurm nextflow配置注意事项下期预告 生信工作流框架搭建 | 04-nextflow与Slurm高性能计算 本篇为biodoge《生信工作流框架搭建》系列笔记的第5篇&#xff0c;该系列将持续更新。 前情…

评价基于GPT和Bert的方法并用于生信文本识别PPI

检测蛋白质-蛋白质相互作用&#xff08;PPI&#xff09;对于理解遗传机制、疾病发病机制和药物设计至关重要。然而&#xff0c;随着生物医学文献的快速增长&#xff0c;越来越需要自动和准确地提取PPI以促进科学知识发现。预训练语言模型&#xff0c;例如生成式预训练Transform…

Limma | 三个组的差异分析怎么分析做呢!?~

1写在前面 高考结束了&#xff0c;不知道各位考生考的怎么样&#xff0c;这种时候总是几家欢喜几家忧&#xff0c;但这也是实现阶级流动的最佳机会。&#x1f914; 回想自己高考过去10几年了&#xff0c;不能说学了医后悔吧&#xff0c;只能说后悔至极&#xff0c;苦不堪言啊&a…

生信分析案例 Python简明教程 | 视频13

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在&#xff1a;https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

生信分析案例 Python简明教程 | 视频12

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在&#xff1a;https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

生信分析案例 Python简明教程 | 视频11

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在&#xff1a;https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

12款好用的Visual Studio插件,最后一款良心推荐

工欲善其事&#xff0c;必先利其器,整理的一些我必装的12款Visual Studio插件&#xff0c;希望你们能get到。 效率工具前文传送门&#xff1a; adb常用命令总结 程序员你可以考虑安装的15款谷歌插件 推荐20套实战源码 99%的人不知道搜索引擎的6个技巧 12款好用的Visual Stu…

七年磨一剑!苹果王炸产品Vision Pro诞生,未来已来

这是第一款「不见却可透见」的苹果产品 等了整整七年&#xff01;2023年6月5日&#xff0c;WWDC23大会上&#xff0c;苹果发布首款头显Vision Pro&#xff0c;Vision Pro 可以算是苹果公司自 2015 年 Apple Watch 首次亮相以来最大的硬件产品发布&#xff0c;或许它会彻底改变数…

chatgpt赋能python:人机对话Python——让交互更智能

人机对话Python——让交互更智能 随着科技的不断发展&#xff0c;人类与机器之间的交互方式也在不断改变。从最初的键盘输入和鼠标点击&#xff0c;到现在的语音识别和手势交互&#xff0c;我们与计算机之间的交互方式日益智能化和自然化。人机对话技术就是其中一种重要的交互…

紫砂典故之“蔓生石瓢”

紫砂典故之“蔓生石瓢”

如何区分纯正紫砂底料

首先大家要区分一个概念&#xff0c;就是纯正的底料和调砂不是同一个概念&#xff0c;调砂其实是紫泥中的石英颗粒&#xff0c;调砂可以增强泥料的可塑性&#xff0c;增强了透气性&#xff0c;以及增加质感。纯的底料&#xff0c;不是那么坚硬&#xff0c;经过一到两天&#xf…

18把紫砂壶上了邮票:今起来厦展出

制壶高工沈龙娣&#xff0c;花了1年时间&#xff0c;纯手工打造了18把紫砂壶&#xff0c;然后由当代著名花鸟画家张贤明院长赴阳羡亲绘壶身&#xff0c;最后请中国第一批省级大师鲍仲 梅篆刻。最为难得的是&#xff0c;这套紫砂壶还上了邮票&#xff0c;由国家邮政总局限量发行…

怎么看安装包是什么bit的_什么是紫砂壶太阳线,怎么看?-紫砂壶

经常能在紫砂壶内的底部&#xff0c;见到一种由中心向四周呈发散状&#xff0c;如太阳光芒线的泥痕&#xff0c;称之为“太阳线”。 常见的模具壶&#xff0c;一般都是以“外模内挡”的成型方式为主&#xff0c;“挡”成后&#xff0c;壶的内壁表面会因受力不均而出现凹凸不平&…

用计算机3d建模做紫砂壶好吗,做3D行业千万别被骗了!建模一点都不难!这一步才是最难的!...

闲来无事逛推特&#xff0c;发现竟然有大神开发了一个建模神器。在像素网格绘制好平面图&#xff0c;再设置下相关参数&#xff0c;就能生成一个3D锤子模型了&#xff0c;妙啊… 其实Windows10系统也有类似的傻瓜式建模软件&#xff0c;叫“画图3D”&#xff0c;它能帮你秒速建…

用计算机3d建模做紫砂壶好吗,3D建模制壶,走在制壶科技的前沿——潘洪强

原标题&#xff1a;3D建模制壶&#xff0c;走在制壶科技的前沿——潘洪强 每一种工艺都有其独特的美&#xff0c;他用艺术的眼光探寻&#xff0c;借助科技缔造出完美的紫砂艺术品。 潘洪强&#xff0c;宜兴紫砂圈的人才。他会做壶&#xff0c;有创意&#xff0c;灵活运用科技的…

扫黑牵出制壶大师,紫砂壶真的那么值钱?

特约作者| 与归 近日&#xff0c;江苏省宜兴市检察院通报了一则消息&#xff1a;该院依法以涉嫌敲诈勒索罪、寻衅滋事罪&#xff0c;对在宜兴市紫砂行业以打假为由&#xff0c;多次采用拘禁、殴打、上门滋扰等手段实施违法犯罪活动的邵洪群、许鹏、吴浩、盛云峰等4名恶势力犯罪…

用手刻出计算机系统,紫砂壶电脑刻字和手工刻字,你看得出吗?

原标题&#xff1a;紫砂壶电脑刻字和手工刻字&#xff0c;你看得出吗&#xff1f; 刻字即用刀在木头或石头上雕刻出文字或者图画&#xff0c;紫砂壶刻字亦是如此&#xff0c;就是在壶壁上雕刻出文字和图画。 “ 紫砂壶陶刻 紫砂壶大体分为光货&#xff0c;花货&#xff0c;筋纹…

最理想的饮茶器具──紫砂壶

饮茶习惯在我国有着悠久的历史&#xff0c;古代文人雅士平时经常聚集一起&#xff0c;且鼎且缶&#xff0c;以啜以饮&#xff0c;视为可获得无穷的情趣。据汉代《华阳国志》&#xff0c;司马相如《凡将篇》和杨雄的《方言》等书中记录了&#xff0c;武王伐纣时就出现将茶作为贡…