# scaffold 项目之登陆验证码
# 简介
项目基于 AJ-Captcha 实现行为验证码,包含滑动拼图、文字点选两种方式,UI 支持弹出和嵌入两种方式。
疑问:为什么采用行为验证码?
相比传统的「传统字符型验证码」的 “展示验证码 - 填写字符 - 比对答案” 的流程来说,「行为验证码」的 “展示验证码 - 操作 - 比对答案” 的流程,用户只需要使用鼠标产生指定的行为轨迹,不需要键盘手动输入,用户体验更好,更加难以被机器识别,更加安全可靠。
# 开始使用
# 实现原理
步骤:
用户访问应用页面,请求显示行为验证码
用户按照提示要求完成验证码拼图 / 点击
用户提交表单,前端将第二步的输出一同提交到后台
验证数据随表单提交到后台后,后台需要调用 captchaService.verification 做二次校验
第 4 步返回校验通过 / 失败到产品应用后端,再返回到前端
# 如何关闭验证码
管理后台的登录界面,默认开启验证码。如果需要关闭验证码,操作如下:
后端的
application-local.yaml配置文件中,将scaffold.captcha.enable设置为false。前端
scaffold-ui-admin-vue3项目,将环境对应的.env配置文件中,将VITE_APP_CAPTCHA_ENABLE设置为false。
ps:如果你不知道环境对应的 .env 配置文件是哪个,就全部改成 false 吧!
# 场景接入
# 后端接入
scaffold-module-system-biz 模块,默认在 pom.xml 已经引入 spring-boot-starter-captcha-plus 依赖,代码如下:
<dependency> | |
<groupId>com.tz.boot</groupId> | |
<artifactId>scaffold-spring-boot-starter-captcha</artifactId> | |
</dependency> |
验证码的配置,在 application.yaml 配置文件中,配置项如下:
aj: | |
captcha: | |
jigsaw: classpath:images/jigsaw # 滑动验证,底图路径,不配置将使用默认图片;以 classpath: 开头,取 resource 目录下路径 | |
pic-click: classpath:images/pic-click # 滑动验证,底图路径,不配置将使用默认图片;以 classpath: 开头,取 resource 目录下路径 | |
cache-type: redis # 缓存 local/redis... | |
cache-number: 1000 # local 缓存的阈值,达到这个值,清除缓存 | |
timing-clear: 180 # local 定时清除过期缓存 (单位秒), 设置为 0 代表不执行 | |
type: blockPuzzle # 验证码类型 default 两种都实例化。 blockPuzzle 滑块拼图 clickWord 文字点选 | |
water-mark: 芋道源码 # 右下角水印文字 (我的水印),可使用 https://tool.chinaz.com/tools/unicode.aspx 中文转 Unicode,Linux 可能需要转 unicode | |
interference-options: 0 # 滑动干扰项 (0/1/2) | |
req-frequency-limit-enable: false # 接口请求次数一分钟限制是否开启 true|false | |
req-get-lock-limit: 5 # 验证失败 5 次,get 接口锁定 | |
req-get-lock-seconds: 10 # 验证失败后,锁定时间间隔 | |
req-get-minute-limit: 30 # get 接口一分钟内请求数限制 | |
req-check-minute-limit: 60 # check 接口一分钟内请求数限制 | |
req-verify-minute-limit: 60 # verify 接口一分钟内请求数限制 |
如果你想修改验证码的 图片,修改 resources/images 目录即可。
验证码的使用例子: CaptchaController.java
package com.tz.scaffold.module.system.controller.admin.captcha; | |
import cn.hutool.core.util.StrUtil; | |
import com.tz.scaffold.framework.common.util.servlet.ServletUtils; | |
import com.tz.scaffold.framework.operatelog.core.annotations.OperateLog; | |
import com.xingyuv.captcha.model.common.ResponseModel; | |
import com.xingyuv.captcha.model.vo.CaptchaVO; | |
import com.xingyuv.captcha.service.CaptchaService; | |
import io.swagger.v3.oas.annotations.Operation; | |
import io.swagger.v3.oas.annotations.tags.Tag; | |
import org.springframework.web.bind.annotation.PostMapping; | |
import org.springframework.web.bind.annotation.RequestBody; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RestController; | |
import javax.annotation.Resource; | |
import javax.annotation.security.PermitAll; | |
import javax.servlet.http.HttpServletRequest; | |
/** | |
* <p> Project: scaffold - CaptchaController </p> | |
* | |
* 验证码 | |
* @author Tz | |
* @date 2024/01/09 23:45 | |
* @version 1.0.0 | |
* @since 1.0.0 | |
*/ | |
@Tag(name = "管理后台 - 验证码") | |
@RestController("adminCaptchaController") | |
@RequestMapping("/system/captcha") | |
public class CaptchaController { | |
@Resource | |
private CaptchaService captchaService; | |
@PostMapping({"/get"}) | |
@Operation(summary = "获得验证码") | |
@PermitAll | |
@OperateLog(enable = false) | |
public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) { | |
assert request.getRemoteHost() != null; | |
data.setBrowserInfo(getRemoteId(request)); | |
return captchaService.get(data); | |
} | |
@PostMapping("/check") | |
@Operation(summary = "校验验证码") | |
@PermitAll | |
@OperateLog(enable = false) | |
public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) { | |
data.setBrowserInfo(getRemoteId(request)); | |
return captchaService.check(data); | |
} | |
public static String getRemoteId(HttpServletRequest request) { | |
String ip = ServletUtils.getClientIP(request); | |
String ua = request.getHeader("user-agent"); | |
if (StrUtil.isNotBlank(ip)) { | |
return ip + ua; | |
} | |
return request.getRemoteAddr() + ua; | |
} | |
} |