springboot+vue使用EasyCaptcha实现简单验证码
一、实现效果
springboot使用EasyCaptcha实现简单验证码,更多api和用法可以去github上查看EasyCaptcha: Java图形验证码,支持gif、中文、算术等类型,可用于Java Web、JavaSE等项目。
二、实现步骤
1、导入依赖
<!-- easy-captcha -->
<dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId><version>1.6.2</version>
</dependency><!-- 解决easy-captcha算术验证码报错问题 -->
<dependency><groupId>org.openjdk.nashorn</groupId><artifactId>nashorn-core</artifactId><version>15.4</version>
</dependency>
我使用的JDK版本是Java21,SpringBoot版本是3.2.0,如果不引入nashorn-core,生成验证码时会报错java.lang.NullPointerException: Cannot invoke "javax.script.ScriptEngine.eval(String)" because "engine" is null。有开发者反馈使用Java 17时也遇到了同样的问题,手动引入nashorn-core后即可解决该问题。
详细堆栈和截图如下:
java.lang.NullPointerException: Cannot invoke "javax.script.ScriptEngine.eval(String)" because "engine" is nullat com.wf.captcha.base.ArithmeticCaptchaAbstract.alphas(ArithmeticCaptchaAbstract.java:42) ~[easy-captcha-1.6.2.jar:na]at com.wf.captcha.base.Captcha.checkAlpha(Captcha.java:156) ~[easy-captcha-1.6.2.jar:na]at com.wf.captcha.base.Captcha.text(Captcha.java:137) ~[easy-captcha-1.6.2.jar:na]at com.fast.alden.admin.service.impl.AuthServiceImpl.generateVerifyCode(AuthServiceImpl.java:72) ~[classes/:na]......
2、后端代码
@RestController
@RequestMapping()
public class EasyCaptchaController {@GetMapping("/specCaptcha")public Result createSpecCaptcha() throws Exception {// png类型SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);//设置验证码字符类型,只有SpecCaptcha和GifCaptcha设置才有效果。//specCaptcha.setCharType(Captcha.TYPE_ONLY_NUMBER);// 设置内置字体//specCaptcha.setFont(Captcha.FONT_1);// 获取验证码字符串,并转为小写,后续可以存储到redis中方便校验String verCode = specCaptcha.text().toLowerCase();String specCaptchaBase64 = specCaptcha.toBase64();return Result.success(specCaptchaBase64);}@GetMapping("/gifsCaptcha")public Result createGifCaptcha() throws Exception {// gif类型GifCaptcha gifCaptcha = new GifCaptcha(130, 48, 5);// 获取验证码字符串,并转为小写,后续可以存储到redis中方便校验String verCode = gifCaptcha.text().toLowerCase();String specCaptchaBase64 = gifCaptcha.toBase64();return Result.success(specCaptchaBase64);}@GetMapping("/chineseCaptcha")public Result createChineseCaptcha() throws Exception {// 中文类型ChineseCaptcha chineseCaptcha = new ChineseCaptcha(130, 48, 5);// 获取验证码字符串,后续可以存储到redis中方便校验String verCode = chineseCaptcha.text();String specCaptchaBase64 = chineseCaptcha.toBase64();return Result.success(specCaptchaBase64);}@GetMapping("/chineseGifCaptcha")public Result createChineseGifCaptcha() throws Exception {// 中文gif类型ChineseGifCaptcha chineseGifCaptcha = new ChineseGifCaptcha(130, 48,5);// 获取验证码字符串,后续可以存储到redis中方便校验String verCode = chineseGifCaptcha.text().toLowerCase();String specCaptchaBase64 = chineseGifCaptcha.toBase64();return Result.success(specCaptchaBase64);}@GetMapping("/arithmeticCaptcha")public Result createArithmeticCaptcha() throws Exception {// 算术类型ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);captcha.setLen(3); // 几位数运算,默认是两位captcha.getArithmeticString(); // 获取运算的公式:3+2=?String text = captcha.text();// 获取运算的结果:5String arithmeticCaptchaBase64 = captcha.toBase64();return Result.success(arithmeticCaptchaBase64);}}
3、前端代码
api
import request from "@/utils/request";
export const getSpecCaptcha = () => {return request({url: "/specCaptcha",method: "get",});
};
export const getGifsCaptcha = () => {return request({url: "/gifsCaptcha",method: "get",});
};
export const getChineseCaptcha = () => {return request({url: "/chineseCaptcha",method: "get",});
};
export const getChineseGifCaptcha = () => {return request({url: "/chineseGifCaptcha",method: "get",});
};
export const getArithmeticCaptcha = () => {return request({url: "/arithmeticCaptcha",method: "get",});
};
vue
<template><div class="captcha"><h1>验证码</h1><div class="easy-captcha"><h3>easy-captcha</h3><span>SpecCaptcha:<img :src="SpecCaptcha" alt="验证码" @click="changeCaptchaImg('Spec')"/></span><span>GifsCaptcha:<img :src="GifsCaptcha" alt="验证码" @click="changeCaptchaImg('Gifs')"/></span><span>ChineseCaptcha:<img:src="ChineseCaptcha"alt="验证码"@click="changeCaptchaImg('Chinese')"/></span><span>ChineseGifCaptcha:<img:src="ChineseGifCaptcha"alt="验证码"@click="changeCaptchaImg('ChineseGif')"/></span><span>ArithmeticCaptcha:<img:src="ArithmeticCaptcha"alt="验证码"@click="changeCaptchaImg('Arithmetic')"/></span></div></div>
</template><script setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
import {getSpecCaptcha,getGifsCaptcha,getChineseCaptcha,getChineseGifCaptcha,getArithmeticCaptcha,
} from "@/api/captcha/index";
import { ElMessage } from "element-plus";const SpecCaptcha = ref("");
const GifsCaptcha = ref("");
const ChineseCaptcha = ref("");
const ChineseGifCaptcha = ref("");
const ArithmeticCaptcha = ref("");const captchaRefs = {Spec: SpecCaptcha,Gifs: GifsCaptcha,Chinese: ChineseCaptcha,ChineseGif: ChineseGifCaptcha,Arithmetic: ArithmeticCaptcha,
};const getCaptcha = async (captchaType) => {try {let res;switch (captchaType) {case "Spec":res = await getSpecCaptcha();break;case "Gifs":res = await getGifsCaptcha();break;case "Chinese":res = await getChineseCaptcha();break;case "ChineseGif":res = await getChineseGifCaptcha();break;case "Arithmetic":res = await getArithmeticCaptcha();break;default:throw new Error("Invalid captcha type");}captchaRefs[captchaType].value = res.data;} catch (error) {console.error(`获取 ${captchaType} 验证码时出错:`, error);ElMessage.error(`获取 ${captchaType} 验证码时出错,请稍后再试`);}
};const changeCaptchaImg = async (captchaType) => {await getCaptcha(captchaType);
};const revokeCaptchaUrls = () => {for (const captchaType in captchaRefs) {if (captchaRefs[captchaType].value) {URL.revokeObjectURL(captchaRefs[captchaType].value);}}
};onMounted(async () => {await Promise.all([getCaptcha("Spec"),getCaptcha("Gifs"),getCaptcha("Chinese"),getCaptcha("ChineseGif"),getCaptcha("Arithmetic"),]);
});onBeforeUnmount(() => {revokeCaptchaUrls();
});
</script><style lang="scss" scoped></style>