一、登录流程
1- 进入登录页面,调用 com.ruoyi.web.controller.common.CaptchaController 类中的
captchaImage 方法,生成base64的图片 以及 UUID
2- 提交 登录信息 + 验证码 + uuid 比对
错误:返回错误信息,删除缓存的验证码
成功: 拿到token,加入缓存
二、验证码生成
1-前台代码配置
前端login 页面中,图片如下:
<div class="login-code"><img :src="codeUrl" @click="getCode" class="login-code-img"/></div>
methods 函数中,captchaEnabled 是验证码是否开启的属性,以及注册功能开关,定义在data中如下:
data() {// 验证码开关captchaEnabled: true,// 注册开关register: false,redirect: undefined};},
在页面 created 时候 ,调用getCode 生成验证码,返回页面
created() {this.getCode();this.getCookie();
}methods: {getCode() {getCodeImg().then(res => {this.captchaEnabled = res.data.captchaEnabled === undefined ? true : res.data.captchaEnabled;if (this.captchaEnabled) {this.codeUrl = "data:image/gif;base64," + res.data.img;this.loginForm.uuid = res.data.uuid;}});},....................
src\api\login.js 中,请求后台
// 获取验证码
export function getCodeImg() {return request({url: '/captchaImage',headers: {isToken: false},method: 'get',timeout: 20000})
}
返回如下:
{"code": 200,"msg": "操作成功","data": {"img": "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","captchaEnabled": true,"uuid": "2f329cd71b96426cac682791a058a3a6"}
}
2-后台代码配置
系参数配置 菜单中,sys.account.captchaEnabled 设置了关于验证码是否开启的配置
后台接口: com.ruoyi.web.controller.common.CaptchaController 类中,逻辑如下:
1- 判断验证码功能是否启用
redis缓存中获取 sys.account.captchaEnabled 判断是否开启验证码 ,配置如上截图
2- 生成uuid
这样根据uuid 就能在缓存中找到验证码
String uuid = IdUtil.simpleUUID();String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;3- 生成验证码
CaptchaType captchaType = captchaProperties.getType();
CaptchaType :ruoyi-admin中yml配置 (验证码类型、干扰线等等配置)captcha:# 页面 <参数设置> 可开启关闭 验证码校验# 验证码类型 math 数组计算 char 字符验证type: MATH# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰category: CIRCLE# 数字验证码位数numberLength: 1# 字符验证码长度charLength: 4
4- spel表达式 (spel 很强大 可以自己看看)
如果是数字,计算结果
if (isMath) {ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(StringUtils.remove(code, "="));code = exp.getValue(String.class); }5- 保存到缓存中,设置过期时间 RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));详细代码如下:
/*** 生成验证码*/@GetMapping("/captchaImage")public R<Map<String, Object>> getCode() {Map<String, Object> ajax = new HashMap<>();//redis缓存中获取 sys.account.captchaEnabledboolean captchaEnabled = configService.selectCaptchaEnabled();ajax.put("captchaEnabled", captchaEnabled);//是否需要验证码if (!captchaEnabled) {return R.ok(ajax);}// 保存验证码信息String uuid = IdUtil.simpleUUID();String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;// 生成验证码CaptchaType captchaType = captchaProperties.getType();boolean isMath = CaptchaType.MATH == captchaType;Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());captcha.setGenerator(codeGenerator);captcha.createCode();String code = captcha.getCode();if (isMath) {ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(StringUtils.remove(code, "="));code = exp.getValue(String.class);}RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));ajax.put("uuid", uuid);ajax.put("img", captcha.getImageBase64());return R.ok(ajax);}
三、手机验证、短信验证
com.ruoyi.web.controller.common.CaptchaController 类中还有其他验证方法:
smsCaptcha 短信验证码captchaEmail 邮箱验证码都是保存到redis中,然后通过uuid拿出来校验,和上门逻辑一样
/*** 短信验证码** @param phonenumber 用户手机号*/@GetMapping("/captchaSms")public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber;String code = RandomUtil.randomNumbers(4);RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));// 验证码模板id 自行处理 (查数据库或写死均可)String templateId = "";LinkedHashMap<String, String> map = new LinkedHashMap<>(1);map.put("code", code);SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);if (!"OK".equals(smsResponse.getCode())) {log.error("验证码短信发送异常 => {}", smsResponse);return R.fail(smsResponse.getMessage());}return R.ok();}/*** 邮箱验证码** @param email 邮箱*/@GetMapping("/captchaEmail")public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {if (!mailProperties.getEnabled()) {return R.fail("当前系统没有开启邮箱功能!");}String key = CacheConstants.CAPTCHA_CODE_KEY + email;String code = RandomUtil.randomNumbers(4);RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));try {MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");} catch (Exception e) {log.error("验证码短信发送异常 => {}", e.getMessage());return R.fail(e.getMessage());}return R.ok();}