验证码响应结果分析
首先从前端开始进行分析,进入到登录页面,打开开发者工具(f12),找到 network,f5 刷新一下页面,然后,筛选一下,筛选内容为 Fetch/XHR
:
你会发现列表中有两项内容,我们只需要查看 captchaImage
即可,从名字就可以看出是验证码图片的意思,然后我们查看这个响应结果是什么,响应结果
{ "msg": "操作成功", "img": "...", "code": 200, "captchaEnabled": true, "uuid": "f17217c9743a445298ec85e317f29537" }
- captchaEnabled: 验证码是否需要开启,true 开启,false 不开启
- img:Base64 编码的图片(如果返回二进制会乱码不好理解,前端可以将 Base64 渲染成为图片)
- uuid:整个系统的 securityId(登录后端有个 session,返回给前端存储到 Cookie 当中,每次带着 Cookie 服务端就知道你已经登录过了,这是传统的方式与做法,ruoyi 使用的是 JWT,但是和传统的 Session 与 Cookie 差不多,换汤不换药)
?> Base64 字符串转图片: https://tool.jisuapi.com/base642pic.html
验证码生成接口分析
通过如上的介绍我们其实已经拿到了验证码接口的名称了,复制一下,去后端当中全局搜这个名字(ctrl + shift + f),会出现如下结果:
到这里我其实介绍了一下,遇到了新项目如何去找接口的位置,这是我比较推荐的一种方式,其它方式就是自己去后端工程当中进行搜寻,这种如果项目比较小还好,太大了我还是推荐我第一种推荐的方式也是现在一直在用的方式进行接口定位。
CaptchaController
captchaImage 方法解刨,如下代码首先去确认了一下验证码是否需要开启:
boolean captchaEnabled = configService.selectCaptchaEnabled();
如果为 false 直接返回响应结果,则前端没有验证码需要进行填写。
!> 这个结果是可以去数据库更改,但是数据库更改了不会立马生效因为更改之前的配置结果保存在 Redis 有一份,所以还需要去删除掉 Redis 在重新加载才会生效
如果 captchaEnabled
为 true,继续往下走,生成验证码,验证码类型分为 math
,char
,根据不同的类型去生成,captchaType
从 RuoYiConfig
中获取,我们来看看 RuoYiConfig 是什么:
可以看到是从我们的外部配置文件动态装配进来的我们去看看这个文件内容的大致结构,其实就是从 application.yml 中获取,我们自定义了一个关键词为 tienchin
在下面配置了一个 captchaType
:
math
数值计算的验证码也就是需要自己根据生成的验证码自己计算正确的结果,例如,7+7=?, 那么底层是如何解析这个正确的答案的呢,关键代码如下:
8+1=?@9,根据 @
截取,前面部分的返回给前端,后面的正确答案会放入 Redis 进行存储,然后通过 Base64 转换返回给前端了。
char
char 就是典型的验证码形式,就是将一串字符以一张图片的形式展示给用户进行填写,我们将正确的验证码结果,存储在 Redis,登录的时候拿着用户输入的与我们 Redis 存在的进行对比即可完成校验。
最终 captchaImage
接口各个部分的代码解释如下:
/** * 生成验证码 */ @GetMapping("/captchaImage") public AjaxResult getCode() { // 创建一个 AjaxResult 这个是用于返回响应结果是实体类对象 AjaxResult ajax = AjaxResult.success(); // 查看验证码的配置是否开启了验证码 boolean captchaEnabled = configService.selectCaptchaEnabled(); // 将标志写入到响应结果中 ajax.put("captchaEnabled", captchaEnabled); // 如果没有开启则直接返回告诉前端 if (!captchaEnabled) { return ajax; } // 得到一个UUID String uuid = IdUtils.simpleUUID(); // 生成验证码的Redis保存Key String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; // 定义变量 String capStr, code = null; // 定义一个缓冲的图片流用于将验证码写给前端使用转换为流的形式 BufferedImage image = null; // 生成验证码 String captchaType = RuoYiConfig.getCaptchaType(); if ("math".equals(captchaType)) { // 逻辑略过(因为太简单) String capText = captchaProducerMath.createText(); capStr = capText.substring(0, capText.lastIndexOf("@")); code = capText.substring(capText.lastIndexOf("@") + 1); image = captchaProducerMath.createImage(capStr); } else if ("char".equals(captchaType)) { // 逻辑略过(因为太简单) capStr = code = captchaProducer.createText(); image = captchaProducer.createImage(capStr); } // 写入Redis redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); // 转换流信息写出,创建了一个输出流 FastByteArrayOutputStream os = new FastByteArrayOutputStream(); try { // 逻辑略过(因为太简单) assert image != null; ImageIO.write(image, "jpg", os); } catch (IOException e) { // 逻辑略过(因为太简单) return AjaxResult.error(e.getMessage()); } // 逻辑略过(因为太简单) ajax.put("uuid", uuid); ajax.put("img", Base64.encode(os.toByteArray())); return ajax; }
?> 觉得逻辑清晰写的不错,还请麻烦给个关注与点赞支持一下博主,这将成为博主更新的动力。