SpringBoot实现简单图形验证码
项目场景:
为防止验证系统被暴力破解,很多系统都增加了验证码效验,业内比较安全的是短信验证码,图形验证码是最传统且应用最广泛的验证码类型,原理是在服务端生成随机字符串并渲染成图片,用户需要识别图片中的字符并输入。图形验证码实现简单,对用户体验影响较小,是中小型应用的理想选择。
实现步骤
需要集成redis,用于记录验证码的id
核心代码CodeUtil:
package com.test.utils;import com.test.constant.CommonConstant;
import com.test.form.CaptchaVo;
import lombok.Cleanup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Encoder;import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;@Service
public class CodeUtil {/****/private Random random = new Random();/*** 随机产生的字符串*/private String randString = "abcdefghjklmnopqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWXYZ";/*** 图片宽*/private int width = 110;/*** 图片高*/private int height = 40;/*** 干扰线数量*/private int lineSize = 10;/*** 随机产生字符数量*/private int stringNum = 4;@Autowiredprivate RedisTemplate<String, String> redisTemplate;/*** 获得字体*/private Font getFont(){return new Font("Fixedsys", Font.CENTER_BASELINE,25);}/*** 获得颜色*/@SuppressWarnings("AlibabaUndefineMagicConstant")private Color getRandColor(int fc, int bc){if(fc > 255) {fc = 255;}if(bc > 255) {bc = 255;}int r = fc + random.nextInt(bc-fc-16);int g = fc + random.nextInt(bc-fc-14);int b = fc + random.nextInt(bc-fc-18);return new Color(r,g,b);}/*** 生成随机图片*/public CaptchaVo getRandcode(HttpServletResponse response) {try {// 生成一个随机标识符String captchaKey = UUID.randomUUID().toString().replace("-", "");//BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类BufferedImage image = new BufferedImage(width,height, BufferedImage.TYPE_INT_BGR);//产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作Graphics g = image.getGraphics();g.fillRect(0, 0, width, height);g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE,20));g.setColor(getRandColor(110, 133));//绘制干扰线for(int i=0;i<=lineSize;i++){drowLine(g);}//绘制随机字符String randomString = "";for(int i=1;i<=stringNum;i++){randomString=drowString(g,randomString,i);}System.out.println("后台代码"+randomString);System.out.println("标识"+captchaKey);// 存储验证码storeCaptcha(captchaKey, randomString);g.dispose();@Cleanup ByteArrayOutputStream outputStream = new ByteArrayOutputStream();//将内存中的图片通过流动形式输出到客户端ImageIO.write(image, "JPEG", outputStream);// 对字节数组Base64编码BASE64Encoder encoder = new BASE64Encoder();String base64Img = "data:image/jpeg;base64,"+encoder.encode(outputStream.toByteArray()).replace("\n","").replace("\r","");CaptchaVo captchaVo = new CaptchaVo();captchaVo.setCaptchaKey(captchaKey);captchaVo.setBase64Img(base64Img);captchaVo.setExpire(CommonConstant.CAPTCHA_EXPIRATION);return captchaVo;}catch (Exception e){e.getStackTrace();}return null;}/*** 绘制字符串*/private String drowString(Graphics g, String randomString, int i){g.setFont(getFont());g.setColor(new Color(random.nextInt(101),random.nextInt(111),random.nextInt(121)));String rand = String.valueOf(getRandomString(random.nextInt(randString.length())));randomString +=rand;g.translate(random.nextInt(3), random.nextInt(3));g.drawString(rand, 17*i, 25);return randomString;}/*** 绘制干扰线*/private void drowLine(Graphics g){int x = random.nextInt(width);int y = random.nextInt(height);int xl = random.nextInt(13);int yl = random.nextInt(15);g.drawLine(x, y, x+xl, y+yl);}/*** 获取随机的字符*/public String getRandomString(int num){return String.valueOf(randString.charAt(num));}// 存储验证码public void storeCaptcha(String captchaKey, String captchaCode) {redisTemplate.opsForValue().set("CAPTCHA:" + captchaKey,captchaCode,CommonConstant.CAPTCHA_EXPIRATION,TimeUnit.SECONDS);}// 验证验证码public boolean validateCaptcha(String captchaKey, String userInputCaptcha) {String key = "CAPTCHA:" + captchaKey;String storedCaptcha = redisTemplate.opsForValue().get(key);if (storedCaptcha != null && storedCaptcha.equalsIgnoreCase(userInputCaptcha)) {// 验证成功后立即删除,防止重复使用redisTemplate.delete(key);return true;}return false;}
}
Controller验证:
package com.test.controller;import com.google.code.kaptcha.Producer;
import com.test.utils.CodeUtil;
import com.test.utils.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import sun.misc.BASE64Encoder;import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.UUID;@RestController
@Api(tags = "验证码接口")
@RequestMapping("/captcha")
public class CaptchaController {@Autowiredprivate CodeUtil codeUtil;@ApiOperation(value = "登录")@GetMapping("/login")public Result<?> login(String captcha, String captchaKey, HttpServletRequest request) {// 验证验证码if (!codeUtil.validateCaptcha(captchaKey, captcha)) {return Result.error("验证码错误或已过期");}return Result.ok("登录成功");}@ApiOperation(value = "获取验证码")@GetMapping("/image")public Result<?> getImageCaptcha2(HttpServletResponse response, HttpServletRequest request) throws IOException {return Result.ok(codeUtil.getRandcode(response));}}
效果截图:
把base64转图片:
主要逻辑:
实际使用把获取到的base64设置到图片src属性里
<img src="base64的值" alt="验证码(点击刷新)" >
登录时,把获取到的随机字符和验证码一块提交。
这里是用的原生代码写的,也可以依赖第三方库实现:
https://blog.csdn.net/weixin_44984196/article/details/148760595