【SpringBoot登录】设置图片验证及2FA双重身份验证两种方式

1. 登录接口

  • 用户登录模块的思路,先校验验证码是否匹配,然后校验用户名,密码是否匹配,匹配则用StpUtil生成token并返回。
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import dev.samstevens.totp.exceptions.QrGenerationException;
import im.gy.zfile.core.util.AjaxJson;
import im.gy.zfile.module.config.model.dto.SystemConfigDTO;
import im.gy.zfile.module.config.service.SystemConfigService;
import im.gy.zfile.module.login.model.enums.LoginVerifyModeEnum;
import im.gy.zfile.module.login.model.request.VerifyLoginTwoFactorAuthenticatorRequest;
import im.gy.zfile.module.login.model.result.LoginTwoFactorAuthenticatorResult;
import im.gy.zfile.module.login.model.result.LoginVerifyImgResult;
import im.gy.zfile.module.login.request.UserLoginRequest;
import im.gy.zfile.module.login.service.ImgVerifyCodeService;
import im.gy.zfile.module.login.service.TwoFactorAuthenticatorVerifyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;import javax.validation.Valid;/*** 登录注销相关接口*/
@Api(tags = "登录模块")
@ApiSort(1)
@RestController
@RequestMapping("/admin")
public class LoginController {@Resourceprivate SystemConfigService systemConfigService;@Resourceprivate ImgVerifyCodeService imgVerifyCodeService;@Resourceprivate TwoFactorAuthenticatorVerifyService twoFactorAuthenticatorVerifyService;@ApiOperationSupport(order = 1, ignoreParameters = {"zfile-token"})@ApiOperation("登录")@PostMapping("/login")public AjaxJson<?> doLogin(@Valid @RequestBody UserLoginRequest userLoginRequest) {String verifyCode = userLoginRequest.getVerifyCode();String verifyCodeUUID = userLoginRequest.getVerifyCodeUUID();SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();LoginVerifyModeEnum loginVerifyMode = systemConfig.getLoginVerifyMode();String loginVerifySecret = systemConfig.getLoginVerifySecret();if (ObjectUtil.equals(loginVerifyMode, LoginVerifyModeEnum.TWO_FACTOR_AUTHENTICATION_MODE)) {twoFactorAuthenticatorVerifyService.checkCode(loginVerifySecret, verifyCode);} else if (ObjectUtil.equals(loginVerifyMode, LoginVerifyModeEnum.IMG_VERIFY_MODE)) {imgVerifyCodeService.checkCaptcha(verifyCodeUUID, verifyCode);}if (ObjectUtil.equals(systemConfig.getUsername(), userLoginRequest.getUsername()) &&ObjectUtil.equals(systemConfig.getPassword(), SecureUtil.md5(userLoginRequest.getPassword()))) {StpUtil.login("admin");SaTokenInfo tokenInfo = StpUtil.getTokenInfo();return AjaxJson.getSuccess("登录成功", tokenInfo);}return AjaxJson.getError("登录失败,账号或密码错误");}@ApiOperationSupport(order = 2)@ApiOperation(value = "注销")@PostMapping("/logout")public AjaxJson<?> logout() {StpUtil.logout();return AjaxJson.getSuccess("注销成功");}@ApiOperationSupport(order = 3)@ApiOperation(value = "生成 2FA")@GetMapping("/2fa/setup")public AjaxJson<LoginTwoFactorAuthenticatorResult> setupDevice() throws QrGenerationException {LoginTwoFactorAuthenticatorResult loginTwoFactorAuthenticatorResult = twoFactorAuthenticatorVerifyService.setupDevice();return AjaxJson.getSuccessData(loginTwoFactorAuthenticatorResult);}@ApiOperationSupport(order = 4)@ApiOperation(value = "2FA 验证并绑定")@PostMapping("/2fa/verify")public AjaxJson<?> deviceVerify(@Valid @RequestBody VerifyLoginTwoFactorAuthenticatorRequest verifyLoginTwoFactorAuthenticatorRequest) {twoFactorAuthenticatorVerifyService.deviceVerify(verifyLoginTwoFactorAuthenticatorRequest);return AjaxJson.getSuccess();}@ApiOperationSupport(order = 5)@ApiOperation(value = "获取登录验证方式")@GetMapping("/login/verify-mode")public AjaxJson<LoginVerifyModeEnum> loginVerifyMode() {SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();return AjaxJson.getSuccessData(systemConfig.getLoginVerifyMode());}@ApiOperationSupport(order = 6)@ApiOperation(value = "获取图形验证码")@GetMapping("/login/captcha")public AjaxJson<LoginVerifyImgResult> captcha() {LoginVerifyImgResult loginVerifyImgResult = imgVerifyCodeService.generatorCaptcha();return AjaxJson.getSuccessData(loginVerifyImgResult);}@ApiOperationSupport(order = 7)@ApiOperation(value = "检测是否已登录")@GetMapping("/login/check")public AjaxJson<Boolean> checkLogin() {return AjaxJson.getSuccessData(StpUtil.isLogin());}
}

2. 图片验证码服务

  • 图片验证码使用CaptchaUtil生成,并保存在缓存中,采用FIFO更新策略
  • 验证方式是根据用户提供的uuid匹配对应的code,code与用户提供的相同则匹配成功。
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.FIFOCache;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.lang.UUID;
import im.zhaojun.zfile.core.exception.LoginVerifyException;
import im.zhaojun.zfile.module.login.model.result.LoginVerifyImgResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import java.util.Objects;/*** 图片验证码 Service** @author zhaojun*/
@Service
@Slf4j
public class ImgVerifyCodeService {/*** 最大容量为 100 的验证码缓存,防止恶意请求占满内存. 验证码有效期为 60 秒.*/private final FIFOCache<String, String> verifyCodeCache = CacheUtil.newFIFOCache(100,60 * 1000L);/*** 生成验证码,并写入缓存中.** @return  验证码生成结果*/public LoginVerifyImgResult generatorCaptcha() {CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 45, 4, 7);String code = captcha.getCode();String imageBase64 = captcha.getImageBase64Data();String uuid = UUID.fastUUID().toString();verifyCodeCache.put(uuid, code);LoginVerifyImgResult loginVerifyImgResult = new LoginVerifyImgResult();loginVerifyImgResult.setImgBase64(imageBase64);loginVerifyImgResult.setUuid(uuid);return loginVerifyImgResult;}/*** 对验证码进行验证.** @param   uuid*          验证码 uuid** @param   code*          验证码** @return  是否验证成功*/public boolean verifyCaptcha(String uuid, String code) {String expectedCode = verifyCodeCache.get(uuid);return Objects.equals(expectedCode, code);}/*** 对验证码进行验证, 如验证失败则抛出异常** @param   uuid*          验证码 uuid** @param   code*          验证码*/public void checkCaptcha(String uuid, String code) {boolean flag = verifyCaptcha(uuid, code);if (!flag) {throw new LoginVerifyException("验证码错误或已失效.");}}}

3. 双因素认证服务

  • 验证使用verifier校验用户提供的secret和code
package im.zhaojun.zfile.module.login.service;import dev.samstevens.totp.code.CodeVerifier;
import dev.samstevens.totp.exceptions.QrGenerationException;
import dev.samstevens.totp.qr.QrData;
import dev.samstevens.totp.qr.QrDataFactory;
import dev.samstevens.totp.qr.QrGenerator;
import dev.samstevens.totp.secret.SecretGenerator;
import im.zhaojun.zfile.core.exception.LoginVerifyException;
import im.zhaojun.zfile.module.login.model.request.VerifyLoginTwoFactorAuthenticatorRequest;
import im.zhaojun.zfile.module.login.model.result.LoginTwoFactorAuthenticatorResult;
import im.zhaojun.zfile.module.config.model.dto.SystemConfigDTO;
import im.zhaojun.zfile.module.login.model.enums.LoginVerifyModeEnum;
import im.zhaojun.zfile.module.config.service.SystemConfigService;
import org.springframework.stereotype.Service;import javax.annotation.Resource;import static dev.samstevens.totp.util.Utils.getDataUriForImage;/*** 2FA 双因素认证 Service** @author zhaojun*/
@Service
public class TwoFactorAuthenticatorVerifyService {@Resourceprivate SecretGenerator secretGenerator;@Resourceprivate QrDataFactory qrDataFactory;@Resourceprivate QrGenerator qrGenerator;@Resourceprivate CodeVerifier verifier;@Resourceprivate SystemConfigService systemConfigService;/*** 生成 2FA 双因素认证二维码和密钥** @return  2FA 双因素认证二维码和密钥* @throws  QrGenerationException   二维码生成异常*/public LoginTwoFactorAuthenticatorResult setupDevice() throws QrGenerationException {// 生成 2FA 密钥String secret = secretGenerator.generate();QrData data = qrDataFactory.newBuilder().secret(secret).issuer("ZFile").build();// 将生成的 2FA 密钥转换为 Base64 图像字符串String qrCodeImage = getDataUriForImage(qrGenerator.generate(data),qrGenerator.getImageMimeType());return new LoginTwoFactorAuthenticatorResult(qrCodeImage, secret);}/*** 验证 2FA 双因素认证是否正确,正确则进行绑定.** @param   verifyLoginTwoFactorAuthenticatorRequest*          2FA 双因素认证请求参数*/public void deviceVerify(VerifyLoginTwoFactorAuthenticatorRequest verifyLoginTwoFactorAuthenticatorRequest) {String secret = verifyLoginTwoFactorAuthenticatorRequest.getSecret();String code = verifyLoginTwoFactorAuthenticatorRequest.getCode();if (verifier.isValidCode(secret, code)) {SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();systemConfig.setLoginVerifyMode(LoginVerifyModeEnum.TWO_FACTOR_AUTHENTICATION_MODE);systemConfig.setLoginVerifySecret(secret);systemConfigService.updateSystemConfig(systemConfig);} else {throw new LoginVerifyException("验证码不正确");}}/*** 验证 2FA 双因素认证.** @param   loginVerifySecret*          2FA 双因素认证密钥** @param   verifyCode*          2FA 双因素认证验证码*/public void checkCode(String loginVerifySecret, String verifyCode) {if (!verifier.isValidCode(loginVerifySecret, verifyCode)) {throw new LoginVerifyException("验证码错误或已失效");}}}

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

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

相关文章

13 Redis-- MySQL 和 Redis 的数据一致性

Redis-- MySQL 和 Redis 的数据一致性 先抛一下结论&#xff1a;在满足实时性的条件下&#xff0c;不存在两者完全保存一致的方案&#xff0c;只有最终一致性方案。

Go语言时间包time中的可用函数和方法 定义 总结和使用示例

Go语言时间包time中的可用函数和方法 Constants 时间常量 这个就是常用的时间格式布局的常量定义 这个在我们对时间进行解析或者转换为字符串是非常重要&#xff0c; 如果是下面的这些类型之一的可用直接 time.XXX 就可使用&#xff0c; 如常见的标准时间格式 年月日时分秒(2…

FlinkX学习

FlinkX学习 FlinkX安装 由于flinkx已经改名chunjun 官网已不存在 (https://gitee.com/lugela/flinkx#flinkx)这里可以看到flinkx的操作文档 1、上传并解压 unzip flinkx-1.10.zip -d /usr/local/soft/2、配置环境变量 FLINKX_HOME/usr/local/soft/flinkx-1.10 export PATH$F…

APP提交到AppStore时报错Asset validation failed (90161)

使用XCode打包并上传到App Store Connect时&#xff0c;收到 “Asset validation failed (90161): Invalid Provisioning Profile” 错误&#xff0c;表明你在上传应用程序到 App Store Connect 时使用了错误的配置文件。以下是解决这个问题的详细步骤&#xff1a; 步骤1&…

STL-迭代器

1.迭代器 1.1正向迭代器 正向迭代器是用一个类封装的&#xff0c;迭代器类。例如&#xff1a;在vector&#xff0c;string中的迭代器就相当于一个指针&#xff0c;在list类中用一个类来封装一个节点&#xff0c;实质上也还是一个指针&#xff0c;迭代器就相当于指向一个节点的…

AUTOSAR的基础软件和功能

AUTOSAR的基础软件和功能 AUTOSAR&#xff08;Automotive Open System Architecture&#xff09;是一个开放且标准化的软件架构&#xff0c;旨在为汽车电子控制单元&#xff08;ECU&#xff09;创建一个可复用和模块化的软件基础。AUTOSAR的基础软件&#xff08;Basic Softwar…

网络构建和设计方法_3. 网络设计

1.层次化网络模型设计 层次化网络设计模型&#xff0c;可帮助设计者按照层次设计网络架构&#xff0c;并对不同层次赋予特定网络功能&#xff0c;选择适合的设备/系统。在典型层次化网络结构中&#xff0c;核心层通常选用具备高可用性和性能优化的高端路由器/交换机&#xff1b…

背靠广汽、小马智行,如祺出行打得过滴滴和百度吗?

©自象限原创 作者丨艾AA 编辑丨薛黎 北京时间6月14日凌晨&#xff0c;在特斯拉股东大会上&#xff0c;马斯克阐述了对Robotaxi&#xff08;自动驾驶出租车&#xff09;商业模式的构想——特斯拉不仅会运营自己的无人驾驶出租车车队&#xff0c;还可以让特斯拉车主们的爱…

微服务开发 —— 项目环境搭建篇

环境搭建 Linux 环境搭建 Linux 环境搭建大家可以使用虚拟机 VMware、VirtualBox 等应用创建虚拟机&#xff0c;使用Vagrant也可以快捷搭建虚拟环境&#xff1b;Windows 中有 WSL2&#xff0c;Windows 中的 Docker 也对 WSL 进行了支持&#xff0c;也是一个不错的选择。或者可…

Windows - 像Linux一样使用alias

前言 阅读本文约2分钟 说明 使用doskey命令 设置别名 查看当前目录 doskey lldir 激活conda的spider环境 doskey spiderconda activate spider 退出conda环境 doskey condaqconda deactivate 查看所有别名 doskey /macros

实现设计开挂|如何设计出坚不可摧的网球拍?

数字揭秘 我们发现自己可能偶尔会以过激的方式表达沮丧或愤怒&#xff0c;哪怕是在公共场合。就算是世界级的网球运动员也无法避免偶尔的情绪爆发&#xff0c;他们有时会砸球拍来释放被压抑的情绪或应对来自竞赛的压力。 网球运动员的情绪爆发已被证明是不可避免的。哪怕是包括…

Linux系统相关函数总结

在应用程序当中&#xff0c;有时往往需要去获取到一些系统相关的信息&#xff0c;譬如时间、日期、以及其它一些系统相关信息&#xff0c;本章将向大家介绍如何通过 Linux 系统调用或 C 库函数获取这些系统信息。除此之外&#xff0c;还会向大家介绍 Linux 系统下的/proc 虚拟文…

浏览器插件利器-allWebPluginV2.0.0.14-beta版发布

allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品&#xff0c;致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX插件直接嵌入浏览器&#xff0c;实现插件加载、界面显示、接口调用、事件回调等。支持谷歌、火狐等浏…

js删除el-table删除新增项,有的已经保存有的未经保存

有时候在弹窗中的弹窗要删除数据,有的是刚新增进来的,没有经过保存就没有id,有的已经保存过就有id 根据情况设定是否为编辑模式,如果为编辑模式就需要进行筛选删除及接口,如果不是编辑模式,只需要进行筛选删除 this.editFlag true; // 为编辑模式// 删除伤亡名单handelDel() …

数据库管理系统(DBMS)

一.数据库管理系统 1.简介 数据库管理系统(Database Management System)是一种操纵和管理数据库的大型软件&#xff0c;用于建立、使用和维护数据库&#xff0c;简称DBMS。它对数据库进行统一的管理和控制&#xff0c;以保证数据库的安全性和完整性。用户通过DBMS访问数据库中…

【PWN · ret2libc | protobuf】[2024CISCN · 华中赛区]protoverflow

套了一层protobuf壳&#xff0c;然后就是简单的ret2libc 参考速递&#xff1a;深入二进制安全&#xff1a;全面解析Protobuf-CSDN博客 前言 第一次遇到protobuf&#xff0c;如果没有了解过&#xff0c;是显然做不出来的。此次复现&#xff0c;也算是点亮了一个技能点 一、什么…

如何用Vue3和Plotly.js实现一个动态3D图的在线展示

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 基于 Plotly.js 的交互式图表动画 应用场景 本代码演示了如何使用 Plotly.js 创建交互式图表动画&#xff0c;其中一个区域填充的区域在给定时间间隔内更新其数据。这种动画可用于可视化时间序列数据或展示数…

【监控】1.prometheus的安装

在 macOS 上部署 Grafana 和 Prometheus 来监控 Java 服务是一个非常实用的操作。以下是详细的步骤&#xff0c;包括如何安装和配置 Prometheus、Grafana 以及在 Java 服务中集成 Prometheus 的客户端库来收集指标数据。 1. 安装 Prometheus 1.1 使用 Homebrew 安装 Promethe…

【创建者模式-单例设计模式】

概述 这种模式设计到一个单一的类&#xff0c;该类负责创建自己的对象&#xff0c;同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象方式&#xff0c;可以直接访问&#xff0c;不需要实例化该类的对象。 单例模式的实现 饿汉式&#xff1a;即类的初始化阶段就…

centOS7网络配置_NAT模式设置

第一步&#xff1a;查看电脑网卡 nat模式对应本地网卡的VMnet 8 &#xff0c;查看对应的IP地址。 第二步&#xff1a;虚拟网络编辑器 打开VMWare&#xff0c;编辑--虚拟网络编辑器&#xff0c;整个都默认设置好了&#xff0c;只需要查看对应的DHCP设置中对应的IP的起始&#…