验证码
这篇解决什么
登录、注册要挡住机器刷接口。读完能对齐前后端开关、改滑动或点选类型、把 captchaVerification 接到登录请求,以及在生产把两边都打开。
默认端口 48080,管理端前缀 /admin-api。本地开发常见两边都关,方便联调;生产必须打开。
示意图:前端先取图、再校验轨迹;登录时把二次校验串交给 AdminAuthServiceImpl。
示意图:左侧滑动拼图 blockPuzzle,右侧文字点选 clickWord。
示意图:点登录后弹出安全验证,通过后再带 captchaVerification 调 /system/auth/login。
为什么用行为验证码
用户拖滑块或按顺序点字,不必再敲扭曲字符。轨迹比静态图片更难被脚本批量识别。
交互流程
前端组件是弹出层(mode="pop")。点登录先出验证码,通过后再提交账号密码。
示意图:check 只证明本次操作通过;登录还要再做一次 verification,串用过即失效。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 取图 | POST /admin-api/system/captcha/get,@PermitAll、@TenantIgnore | ruoyi-office/yudao-module-system/.../controller/admin/captcha/CaptchaController.java |
| 一次校验 | POST /admin-api/system/captcha/check,校验轨迹 | 同上 |
| 二次校验 | 登录、注册把 captchaVerification 交给 captchaService.verification | .../service/auth/AdminAuthServiceImpl.java |
| 入参 | AuthLoginReqVO / AuthRegisterReqVO 继承 CaptchaVerificationReqVO | .../controller/admin/auth/vo/ |
| 登录失败码 | AUTH_LOGIN_CAPTCHA_CODE_ERROR(1_002_000_004) | yudao-module-system-api/.../enums/ErrorCodeConstants.java |
| 注册失败码 | AUTH_REGISTER_CAPTCHA_CODE_ERROR(1_002_000_008) | 同上 |
get / check 未登录就能调。浏览器标识用客户端 IP 加 User-Agent,见 CaptchaController#getRemoteId。
开关打开时,doValidateCaptcha 才用 CodeEnableGroup 强校验 captchaVerification 非空,再调二次校验:
private ResponseModel doValidateCaptcha(CaptchaVerificationReqVO reqVO) {
if (!captchaEnable) {
return ResponseModel.success();
}
ValidationUtils.validate(validator, reqVO, CaptchaVerificationReqVO.CodeEnableGroup.class);
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
return captchaService.verification(captchaVO);
}登录失败会记 LoginResultEnum.CAPTCHA_CODE_ERROR。注册、以及重置密码场景的 sendSmsCode,同样走这条二次校验。
开关
两套开关必须一起看。前端只决定弹不弹;后端决定认不认。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
yudao.captcha.enable | 后端总闸。缺省 true | AdminAuthServiceImpl 的 @Value |
| 开发 / 本地 | 常为 false,方便接口联调 | ruoyi-office/yudao-server/src/main/resources/application-dev.yaml、application-local.yaml |
| 生产 | 必须 true | ruoyi-office/yudao-server/src/main/resources/application-prod.yaml |
VITE_APP_CAPTCHA_ENABLE | 前端是否渲染 Verification | isCaptchaEnable() → ruoyi-office-vben/packages/effects/hooks/src/use-app-config.ts |
| 前端开发 | .env.development 为 false | ruoyi-office-vben/apps/web-antd/.env.development |
| 前端生产 | .env.production 为 true | ruoyi-office-vben/apps/web-antd/.env.production |
| 移动端 | 同一环境变量名 | ruoyi-office-uniapp/env/.env |
# application-prod.yaml
yudao:
captcha:
enable: true# .env.production
VITE_APP_CAPTCHA_ENABLE=true生产必须打开
客户环境和正式发布不要关验证码。只把 false 留在本地 dev / local。生产 YAML 和 .env.production 都写成 true,两边一起生效。
两边要对齐
前端开、后端关:弹层有,登录不校验。前端关、后端开:没有 captchaVerification,登录报验证码不正确。
后端
依赖已在 system 模块。Spring Boot 3 不会自动装配 AJ Captcha,要由配置类导入。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 依赖 | com.anji-plus:captcha-spring-boot-starter | ruoyi-office/yudao-module-system/yudao-module-system-server/pom.xml |
| 自动配置 | @ImportAutoConfiguration(AjCaptchaAutoConfiguration) | .../framework/captcha/config/YudaoCaptchaConfiguration.java |
| Redis 缓存 | cache-type: redis 时注入 StringRedisTemplate | .../framework/captcha/core/RedisCaptchaServiceImpl.java |
| SPI 缓存 | META-INF/services/com.anji.captcha.service.CaptchaCacheService | 同模块 src/main/resources/META-INF/services/ |
| 文字输入 | pictureWord,4 位字母数字 | .../framework/captcha/core/PictureWordCaptchaServiceImpl.java |
| 登录入口 | @RequestMapping("/system/auth"),POST /login | .../controller/admin/auth/AuthController.java |
aj.captcha 写在单体 application.yaml。底图目录当前仓库没有自备资源,缺文件时用组件自带图。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
type | 默认 blockPuzzle。还可 clickWord、pictureWord、default | ruoyi-office/yudao-server/src/main/resources/application.yaml |
cache-type | redis / local。生产用 Redis,多实例才一致 | 同上 |
jigsaw / pic-click | 底图 classpath。可在 resources/images/ 自备 | 同上 |
water-mark | 右下角水印。现网置空,避免露出品牌字 | 同上 |
interference-options | 滑动干扰,0 / 1 / 2 | 同上 |
req-*-limit | get / check / verify 的分钟限流与失败锁定 | 同上 |
换底图时,把图片放进 yudao-module-system-server/src/main/resources/images/jigsaw 或 pic-click,路径与配置一致即可。
Redis 挂了验证码也挂
cache-type: redis 时,取图和校验都写 Redis。本机联调先保证 127.0.0.1:6379 活着,再查登录失败是不是验证码缓存没写上。
前端
管理端主应用是 web-antd。登录、注册、社交登录共用同一套组件。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 登录页 | captchaEnable 为真才挂 Verification,mode="pop" | ruoyi-office-vben/apps/web-antd/src/views/_core/authentication/login.vue |
| 注册 / 社交 | 同样先校验再提交 | 同目录 register.vue、social-login.vue |
| 组件 | blockPuzzle → 滑块,clickWord → 点选 | ruoyi-office-vben/packages/effects/common-ui/src/components/captcha/verification/ |
| API | baseRequestClient.post,不带登录 Token | ruoyi-office-vben/apps/web-antd/src/api/core/auth.ts |
登录页默认 captchaType = 'blockPuzzle'。点登录若开关打开,先 verifyRef.show(),成功回调再 authLogin:
async function handleLogin(values: any) {
if (captchaEnable) {
verifyRef.value.show();
return;
}
await authStore.authLogin('username', values);
}
async function handleVerifySuccess({ captchaVerification }: any) {
await authStore.authLogin('username', {
...(await loginRef.value.getFormApi().getValues()),
captchaVerification,
});
}export async function getCaptcha(data: any) {
return baseRequestClient.post('/system/captcha/get', data);
}
export async function checkCaptcha(data: any) {
return baseRequestClient.post('/system/captcha/check', data);
}移动端
UniApp 登录、注册、短信登录也读 VITE_APP_CAPTCHA_ENABLE。默认 env/.env 为 false。打开后组件在 pages-core/auth/components/verifition/,接口同样打 /system/captcha/get 与 /check。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 登录 | captchaEnabled 控制是否弹出 | ruoyi-office-uniapp/src/pages-core/auth/login.vue |
| 注册 / 短信登录 | 同一开关 | register.vue、code-login.vue |
| API | original: true,直接吃组件协议 | ruoyi-office-uniapp/src/api/login.ts |
后端闸门是全局的,不按终端拆。生产若打开 yudao.captcha.enable,移动端也必须弹出并带上 captchaVerification。
换类型
PC 组件只接两种行为验证码。改登录页常量,并让后端 aj.captcha.type 对上。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
blockPuzzle | 滑动拼图,当前默认 | login.vue 的 captchaType;aj.captcha.type |
clickWord | 文字点选 | Verification 的 verify-points.vue |
pictureWord | 后端 4 位字符图,SPI 已注册 | PictureWordCaptchaServiceImpl |
default | 后端三种都实例化 | aj.captcha.type |
VerificationProps.captchaType 只有 'blockPuzzle' | 'clickWord'。要改成输入字符,需要自己做输入框,把用户输入放到组件协议的 pointJson,不能只改后端 type。
配置与操作
改 yudao.captcha.enable 和 aj.captcha.type 后重启。本地常关。没有验证码 CRUD 菜单,登录页直接调 /system/captcha。
开启见 框架层。
