一、简介
为了防止网站的用户被通过密码典爆破。引入验证码的功能是十分有必要的。而前端的验证码又仅仅是只防君子不防小人。通过burpsuit等工具很容易就会被绕过。所以后端实现的验证码才是对用户信息安全的一大重要保障。
实现思路:
1.引入图形生成的依赖
2.生成随机4字符,并制作成图片
3.对图片进行Base64形式数据进行传输
4.前端显示
二、引入依赖
<!-- 验证码模块-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
三、验证码生成工具类
public class CaptchaUtil {
private static final int WIDTH = 200;
private static final int HEIGHT = 75;
private static final int FONT_SIZE = 36;
private static final String DEFAULT_FONT = "Arial";
/**
* 生成验证码图像.
*
* @param captchaText 验证码原始文本
* @return Base64编码的图像字符串
*/
public static String generateCaptchaImage(String captchaText) {
if (captchaText == null || captchaText.isEmpty()) {
throw new IllegalArgumentException("Captcha text cannot be null or empty.");
}
// 创建图像和图形上下文
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) image.getGraphics();
// 设置背景颜色
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
// 绘制验证码文本
g.setFont(new Font(DEFAULT_FONT, Font.BOLD, FONT_SIZE));
g.setColor(getRandomColor());
g.drawString(captchaText, 45, 50);
// 添加随机线条作为干扰
addNoiseLines(g);
// 关闭图形上下文
g.dispose();
// 将图像转换为Base64编码的字符串
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ImageIO.write(image, "png", baos);
return Base64.getEncoder().encodeToString(baos.toByteArray());
} catch (Exception e) {
throw new RuntimeException("Error generating captcha image", e);
}
}
private static void addNoiseLines(Graphics2D g) {
for (int i = 0; i < 5; i++) {
g.setColor(getRandomColor());
g.drawLine(
getRandomNumber(WIDTH),
getRandomNumber(HEIGHT),
getRandomNumber(WIDTH),
getRandomNumber(HEIGHT)
);
}
}
private static Color getRandomColor() {
return new Color((int) (Math.random() * 255),
(int) (Math.random() * 255),
(int) (Math.random() * 255));
}
private static int getRandomNumber(int bound) {
return (int) (Math.random() * bound);
}
}
四、通过Http Session存放验证码与验证
获取(a-z A-Z 0-9)随机四位的验证码功能:
// 登陆时候获取验证码
@ApiOperation("获取验证码功能")
@GetMapping("/GetCaptcha")
public String GetCaptcha(HttpSession session) {
// 随机生成四位验证码原始数据
String allowedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
String randomString = generateRandomString(allowedChars, 4);
System.out.println("captchaCode " + randomString);
// 将验证码保存到session中
session.setAttribute("captcha", randomString); // 使用方法参数session
String ImageByBase64 = CaptchaUtil.generateCaptchaImage(randomString);
return ImageByBase64;
}
用户登陆时候校验验证码功能:
// 实现登陆功能
@ApiOperation("用户登陆功能")
@PostMapping("/login")
public Result Login(@RequestBody LoginDTO loginDTO, HttpSession session) { // 使用同一个HttpSession参数
String captcha = (String) session.getAttribute("captcha");
log.info("用户调用login方法");
if (loginDTO.getCaptcha() == null || !loginDTO.getCaptcha().equalsIgnoreCase(captcha)) {
session.removeAttribute("captcha");
return Result.error("验证码出错了噢!");
}
// 对密码进行md5加密
String encryptToMD5 = MD5Util.encryptToMD5(loginDTO.getPassword());
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getAccount, loginDTO.getAccount())
.eq(User::getPassword, encryptToMD5);
User user = userService.getOne(lambdaQueryWrapper);
if (user == null) {
return Result.error("很抱歉,查不到此用户");
}
user.setPassword("xxxxx");
return Result.success(user);
}
五、前端显示Base64格式的图片
前端实现注册的表单
<el-tab-pane label="登陆" name="first">
<el-form :model="loginForm" ref="loginFormRef" label-width="80px">
<el-form-item label="用户名:">
<el-input v-model="loginForm.account"></el-input>
</el-form-item>
<el-form-item label="密码:">
<el-input v-model="loginForm.password" show-password></el-input>
</el-form-item>
<el-form-item label="验证码">
<el-input v-model="loginForm.captcha" style="width: 20%;"></el-input>
<img :src="captchaImageUrl" alt="验证码" @click="refreshCaptcha" id="captchaImage">
</el-form-item>
</el-form>
</el-tab-pane>
先设置为空
export default {
data() {
return {
captchaImageUrl: '', // 初始化为一个空字符串
}
},
在点击登陆按钮后,执行getCaptcha函数并设置captchaImageUrl的回显格式为Base64
fetchCaptcha() {
axios.get('/api/user/GetCaptcha')
.then(response => {
this.captchaImageUrl = 'data:image/png;base64,' + response.data;
})
.catch(error => {
console.error('获取验证码失败:', error);
});
},
最后进行测试:
注意:实现完成验证码功能后,需要注意用户的操作。如果登陆失败,刷新页面,切换页面。都需要重新更新验证码!