前后端分离项目中验证码登录方案优化与实现指南
SpringBoot 图形验证码的生成和校验

更新时间为 2021 年 05 月 27 日 14 时 57 分 02 秒,作者是盛夏温暖流年。
系统和业务在不停升级,前后端代码放在一起的项目变得越来越臃肿,已经无法做到快速迭代以及进行职责区分。于是,大家纷纷投入到前后端分离的怀抱中。当实现代码和职责分离后,开发效率越来越高。然而,以前的验证码登录方案需要进行更改。接下来,本文将来看一下 SpringBoot 图形验证码的生成和校验。
目录
1. 编写工具类
package com.cn.beauty.util;
import org.slf4j.Logger;
引入 org.slf4j.LoggerFactory 类;将 org.slf4j.LoggerFactory 类进行引入;把 org.slf4j.LoggerFactory 类给引入进来。
import javax.imageio.ImageIO;
引入 javax.servlet.http 中的 HttpServletRequest 类;
引入 javax.servlet.http.HttpServletResponse;
需要导入 javax.servlet.http 包中的 HttpSession 类。
import java.awt.*;
引入了 Java 的.awt.image 包中的 BufferedImage 类。
import java.util.ArrayList;
import java.util.Random;
公共类 RandomValidateCodeUtil 。
公共静态最终字符串 RANDOMCODEKEY 为 "MEISHANG-LOGIN-RANDOMVALIDATECODEKEY";这是放到 session 中的 key。
此字符串用于随机产生数字与字母组合的字符串。
private int width = 95;// 图片宽
图片的高度为 40,这是一个私有整型变量,其值被设定为 40。
声明一个整型变量 stringNum,其值为 4;这个变量用于随机产生字符的数量。
Logger 被定义为私有静态常量,其值为 LoggerFactory.getLogger(RandomValidateCodeUtil.class);该 Logger 用于记录相关日志信息;它是在 RandomValidateCodeUtil 类的上下文中被创建和使用的。
有一个私有对象 random,它是通过 new Random() 创建的。
/**
* 获得字体
*/
private Font getFont() {
创建一个新的字体对象,该字体使用“Wide Latin”字体族,字体样式为普通(Font.PLAIN),字体大小为 18 。
}
/**
* 获得颜色(粉色为主)
*/
然后将这三个分量值组合成一个颜色对象并返回。
ArrayList colors = new ArrayList();
颜色添加了一个新的颜色,其 RGB 值为 241、158 和 194。
颜色被添加进来,添加的是一个新的颜色,这个新颜色的 RGB 值分别为 255、94 和 226。
创建了一个新的颜色对象,其 RGB 值分别为 255、156 和 177,并将其添加到 colors 集合中。
添加了一种新的颜色,其 RGB 值为 245、152 和 217 。
创建了一个新的 Color 对象,其红色分量为 255,绿色分量为 85,蓝色分量为 81,并将其添加到 colors 中。
添加一种颜色,这种颜色是新创建的 Color(245, 152, 251);
int num = random.nextInt(6);
return colors.get(num);
}
/**
* 生成随机图片
*/
在获取随机码时,需要使用 HttpServletRequest 请求对象和 HttpServletResponse 响应对象。首先获取请求对象,然后从请求对象中获取相关参数或信息,接着根据这些参数或信息生成随机码,最后将生成的随机码通过响应对象返回给客户端。
获取到请求对象 request ,然后通过 request 获取到会话对象 HttpSession ,即 session = request.getSession() 。
BufferedImage 类是一种带有缓冲区的 Image 类。Image 类是用来描述图像信息的类。
创建了一个 BufferedImage 对象,其宽度为 width,高度为 height,类型为 BufferedImage.TYPE_INT_BGR,该对象名为 image。
产生 Image 对象的 Graphics 对象,该 Graphics 对象为 g,它可以在图像上进行各种绘制操作。
g 用 fillRect 方法绘制一个矩形,其左上角坐标为(0, 0),宽度为 width,高度为 height,此操作用于确定图片的大小。
g 设定了一个新的字体,这个字体的名称为“Wide Latin”,字体样式为 Font.PLAIN,字体大小为 18。
设置字体颜色为获取到的随机颜色。
// 添加噪点
float yawpRate = 0.01f;// 噪声率
定义一个整型变量 area ,它的值等于 yawpRate 乘以 width 再乘以 height 的结果并进行强制类型转换为整型。
for (int i = 0; i < area; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
image.setRGB(x, y, random.nextInt(255));
}
// 绘制随机字符
String randomString = "";
for (int i = 1; i <= stringNum; i++) {
randomString = drowString(g, randomString, i);
}
logger.info(randomString);
//将生成的随机字符串保存到session中
session.removeAttribute(RANDOMCODEKEY);
session.setAttribute(RANDOMCODEKEY, randomString);
g.dispose();
try {
// 将内存中的图片通过流动形式输出到客户端
ImageIO.write(image, "JPEG", response.getOutputStream());
} catch (Exception e) {
logger.error("将内存中的图片通过流动形式输出到客户端失败>>>> ", e);
}
}
/**
* 绘制字符串
*/
绘制的过程中涉及到整数 i 。
g.setFont(getFont());
g.setColor(getRandColor());
获取随机数 `random.nextInt(randString)`,将其转换为字符串,赋值给 `rand` 。
.length())));
randomString += rand;
g 进行了一次翻译操作,这个操作的参数分别是两个在 0 到 2 之间(包括 0 和 2)随机生成的整数。
g 绘制字符串 rand,位置在 20 乘以(i 减 1)再加上 3 的横坐标处,纵坐标为 25。
return randomString;
}
/**
* 获取随机的字符
*/
获取一个指定长度的随机字符串。这个长度由参数 num 决定。它会生成一个包含随机字符的字符串。这些随机字符可以是字母、数字或其他可打印字符。通过特定的算法和随机数生成器来实现随机字符串的生成。
获取随机字符串中的指定位置字符,然后将其转换为字符串形式并返回。具体来说,就是获取 randString 这个随机字符串,找到其索引为 num 的位置的字符,再将该字符转换为字符串,最后返回这个字符串。
}
}
2. 编写 Controller 中的接口
/**
* @description 生成验证码
*/
@RequestMapping 的 value 属性值为 "/user/getVerify"
公共的方法用于获取验证。该方法接收一个 HttpServletRequest 请求对象和一个 HttpServletResponse 响应对象作为参数。它的主要作用是进行与验证相关的操作,并将结果返回或进行相应的处理。
try {
//设置相应类型,告诉浏览器输出的内容为图片
response 被设置为 "image/jpeg" 这种内容类型。
//设置响应头信息,告诉浏览器不要缓存此内容
response 设置了一个名为 "Pragma" 的头部,其值为 "No-cache"。
response 被设置了头部信息“Cache-Control”,其值为“no-cache”。
response 设置了一个名为“Expire”的日期头,其值为 0 。
RandomValidateCodeUtil 是一个新创建的对象,名为 randomValidateCode 。
//输出验证码图片方法
获取随机验证码的函数 randomValidateCode 对请求和响应进行了操作,即获取了随机验证码并分别作用于请求和响应。
} catch (Exception e) {
log.error("获取验证码失败>>>>", e);
}
}
/**
* @description 校验验证码
*/
@PostMapping 用于映射到 "/user/checkVerify" 路径
判断是否通过验证,需要传入一个名为 verifyInput 的请求参数以及一个 HttpSession 对象。如果从 session 中获取到的验证码与传入的 verifyInput 相等,则返回 true,否则返回 false。
try {
// 从session中获取随机数
verifyInput 被赋值给了 String 类型的 inputStr 。
从 session 中获取名为 "MEISHANG-LOGIN-RANDOMVALIDATECODEKEY" 的属性,其值为一个字符串,将这个字符串赋值给变量 random 。
if (random == null) {
return false;
}
如果 random 与 inputStr 不区分大小写地相等,那么
return true;
} else {
return false;
}
} catch (Exception e) {
log.error("验证码校验失败", e);
return false;
}
}
3. 获取验证码图片
访问获取验证码接口
通过 127.0.0.1:8605 这个地址来获取用户的验证信息。
效果如下
另一种方法1.添加依赖
com.github.penggle kaptcha 2.3.2 org.springframework.boot spring-boot-starter-web
项目结构
2.全部代码
UserController
package com.yzm.config;
import java.util.HashMap;
引入 javax.annotation.Resource 这个注解;将 javax.annotation.Resource 进行引入;导入 javax.annotation.Resource 这个注解到代码中;把 javax.annotation.Resource 这个注解给引入进来;对 javax.annotation.Resource 进行引入操作;把 javax.annotation.Resource 这个注解引入到程序里;引入了 javax.annotation.Resource 这个注解;将 javax.annotation.Resource 这个注解予以引入;把 javax.annotation.Resource 这个注解引入到相关部分。
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
使用了 Spring 框架的注解 @Controller 来标识这是一个控制器类。它在 Spring 应用程序的开发中起到了关键作用,用于处理 HTTP 请求并返回相应的视图或数据。通过这个注解,Spring 能够识别和管理该类,将其纳入到应用的请求处理流程中,实现业务逻辑的处理和页面的渲染等功能。
使用了 org.springframework.web.bind.annotation.RequestMapping 这个注解。 它用于处理 HTTP 请求映射。 可以将特定的 HTTP 请求路径与处理该请求的方法进行关联。 这样在接收到相应的 HTTP 请求时,就会调用对应的处理方法。
引入 org.springframework.web.bind.annotation.RequestParam;
引入 org.springframework.web.bind.annotation.ResponseBody ;将 org.springframework.web.bind.annotation.ResponseBody 进行引入 ;引入的是 org.springframework.web.bind.annotation.ResponseBody 。
引入 com.google.code.kaptcha.impl 包下的 DefaultKaptcha 类。
import com.yzm.utils.Util;
@Controller
public class TestControler {
@Resource
有一个私有类型的 DefaultKaptcha 叫做 captchaProducer 。
/**
* 登录验证码SessionKey
*/
3. 公共的、静态的且最终的字符串为“login_validate_code”,其用途是登录验证码。
/**
* 登录验证码图片
*/
@RequestMapping 的值为 {"/loginValidateCode"}
在公共方法 loginValidateCode 中,接收了 HttpServletRequest 请求和 HttpServletResponse 响应这两个参数,并且在该方法内部会抛出异常。
Util 对请求、响应、验证码生成器以及登录验证码进行验证代码的操作,即 Util.validateCode(request, response, captchaProducer, LOGIN_VALIDATE_CODE)。
}
/**
* 检查验证码是否正确
*/
@RequestMapping 的路径为 "/checkLoginValidateCode"
@ResponseBody
公共的 HashMap 用于检查登录验证码。它接收一个 HttpServletRequest 请求和一个名为“validateCode”的请求参数字符串。这个方法用于验证登录相关的验证码。
从请求中获取会话,然后从会话中获取名为 LOGIN_VALIDATE_CODE 的属性,最后将该属性转换为字符串并赋值给 String 类型的 loginValidateCode 变量。
HashMap map = new HashMap();
如果登录验证码为空。
map 将 "status" 这个键对应的值设为 null,这表示验证码已经过期。
如果 loginValidateCode 等于 validateCode ,那么
map 将 "status" 这个键与 true 值进行关联,意味着验证码是正确的。
如果 loginValidateCode 不等于 validateCode,那么就……
map 将 "status" 这个键与 false 这个值进行关联,意味着验证码不正确。
}
map.put("code",200);
return map;
}
}
package com.yzm.controller;
import java.util.Properties;
在 org.springframework.context.annotation 包中使用 @Bean 进行注解。
使用 @Component 注解的是一个 Java 类,该类属于 Spring 框架的一部分,用于标识这是一个组件。
import com.google.code.kaptcha.impl.DefaultKaptcha;
引入 com.google.code.kaptcha.util.Config 类。
@Component
public class TestConfig {
@Bean
它可以用于生成验证码等相关操作。
创建了一个名为 defaultKaptcha 的 DefaultKaptcha 对象。
创建了一个 Properties 类的实例,名为 properties 。
// 图片边框
properties 让 "kaptcha.border" 的属性值为 "no";
// 边框颜色
properties 让 "kaptcha.border.color" 属性的值为 "black" 。
//边框厚度
properties 令 "kaptcha.border.thickness" 属性等于 "1" 。
// 图片宽
properties 将 "kaptcha.image.width" 属性设置为 "200"。
// 图片高
设置属性"kaptcha.image.height"的值为"50"。
//图片实现类
properties 令 "kaptcha.producer.impl" 属性的值为 "com.google.code.kaptcha.impl.DefaultKaptcha"。
//文本实现类
properties 将 "kaptcha.textproducer.impl" 属性设置为 "com.google.code.kaptcha.text.impl.DefaultTextCreator"。
//文本集合,验证码值从此集合中获取
properties 把 "kaptcha.textproducer.char.string" 属性设置为 "01234567890" ;
//验证码长度
properties 将“kaptcha.textproducer.char.length”属性设置为“4”。
//字体
properties 把“kaptcha.textproducer.font.names”这个属性设置为“宋体”。
//字体颜色
properties 将 "kaptcha.textproducer.font.color" 属性设置为 "black"。
//文字间隔
properties 将 "kaptcha.textproducer.char.space" 属性设置为 "5"。
//干扰实现类
properties 将 "kaptcha.noise.impl" 属性设置为 "com.google.code.kaptcha.impl.DefaultNoise" 。
//干扰颜色
properties 将 "kaptcha.noise.color" 属性设置为 "blue"。
//干扰图片样式
properties 将 "kaptcha.obscurificator.impl" 属性设置为 "com.google.code.kaptcha.impl.WaterRipple"。
//背景实现类
properties 将 "kaptcha.background.impl" 属性设置为 "com.google.code.kaptcha.impl.DefaultBackground"
//背景颜色渐变,结束颜色
properties 将 "kaptcha.background.clear.to" 属性设置为 "white"。
//文字渲染器
properties 将 "kaptcha.word.impl" 属性设置为 "com.google.code.kaptcha.text.impl.DefaultWordRenderer"
创建了一个名为 config 的对象,该对象是通过 new Config(properties) 语句创建的。
defaultKaptcha 把配置(config)进行了设置。
return defaultKaptcha;
}
}
Util
package com.yzm.utils;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
导入 javax.servlet.ServletOutputStream 类。
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.code.kaptcha.impl.DefaultKaptcha;
public class Util {
/**
* 生成验证码图片
* @param request 设置session
* @param response 转成图片
参数 captchaProducer 为生成图片的方法类。
参数为 validateSessionKey ,它是一个 session 名称。
* @throws Exception
*/
静态的 void 方法 validateCode,它接受 HttpServletRequest 请求、HttpServletResponse 响应、DefaultKaptcha 验证码生成器以及字符串 validateSessionKey 作为参数,并抛出异常。
设置为在很久以前就过期。
response 设置了名为 "Expires" 的日期头,将其值设置为 0 。
设置标准的 HTTP/1.1 无缓存头。
response 设置了头部信息“Cache-Control”,其值为“no-store, no-cache, must-revalidate”。
response 为响应对象,它添加了一个名为 "Cache-Control" 的头部信息,该头部信息的值为 "post-check=0, pre-check=0" 。
设置标准的 HTTP/1.0 无缓存头。
response 为响应对象,通过该对象设置了名为 "Pragma" 的头部信息,其值为 "no-cache" 。
// return a jpeg
设置响应的内容类型为“image/jpeg”。
创建用于该图像的文本。
captchaProducer 生成了字符串 capText 。
将文本存储在会话中。
获取到的 session 调用 setAttribute 方法,将 validateSessionKey 作为键,capText 作为值进行设置。即通过 request 获取到的 session 把 validateSessionKey 这个键与 capText 这个值关联起来。
使用这段文字来创建图像。
BufferedImage bi 是由 captchaProducer 创建的图像,创建的内容为 capText 。
response 获取到输出流,该输出流为 ServletOutputStream 类型,记为 out 。
// write the data out
使用 ImageIO 将 bi 写入为 "jpg" 格式并输出到 out 中。
try {
out.flush();
} finally {
out.close();
}
}
}