验证码的实现方式
kaptcha的实现方式
Constant | description | default |
---|---|---|
kaptcha.border | 图片边框,合法值:yes,no | yes |
kaptcha.border.color | 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. | black |
kaptcha.image.width | 图片宽 | 200 |
kaptcha.image.height | 图片高 | 50 |
kaptcha.producer.impl | 图片实现类 | com.google.code.kaptcha.impl.DefaultKaptcha |
kaptcha.textproducer.impl | 文本实现类 | com.google.code.kaptcha.text.impl.DefaultTextCreator |
kaptcha.textproducer.char.string | 文本集合,验证码值从此集合中获取 | abcde2345678gfynmnpwx |
kaptcha.textproducer.char.length | 验证码长度 | 5 |
kaptcha.textproducer.font.names | 字体 | Arial, Courier |
kaptcha.textproducer.font.size | 字体大小 | 40px. |
kaptcha.textproducer.font.color | 字体颜色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.textproducer.char.space | 文字间隔 | 2 |
kaptcha.noise.impl | 干扰实现类 | com.google.code.kaptcha.impl.DefaultNoise |
kaptcha.noise.color | 干扰 颜色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.obscurificator.impl | 图片样式: | |
水纹 com.google.code.kaptcha.impl.WaterRipple | ||
鱼眼 com.google.code.kaptcha.impl.FishEyeGimpy | ||
阴影 com.google.code.kaptcha.impl.ShadowGimpy | com.google.code.kaptcha.impl.WaterRipple | |
kaptcha.background.impl | 背景实现类 | com.google.code.kaptcha.impl.DefaultBackground |
kaptcha.background.clear.from | 背景颜色渐变,开始颜色 | light grey |
kaptcha.background.clear.to | 背景颜色渐变, 结束颜色 | white |
kaptcha.word.impl | 文字渲染器 | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
kaptcha.session.key | session key | KAPTCHA_SESSION_KEY |
kaptcha.session.date | session date | KAPTCHA_SESSION_DATE |
1. pom文件中导入kaptcha依赖
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
2. 添加Kaptcha配置类
package com.example.demo;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.Properties;
@Component
public class KaptchaConfig {
/**
* 验证码 生成的bean
*/
/*@Bean
public DefaultKaptcha captchaProducer() {
DefaultKaptcha captchaProducer = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "100");
properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "30");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "22");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "6");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
properties.setProperty(Constants.KAPTCHA_BORDER_COLOR, "LIGHT_GRAY");
properties.setProperty(Constants.KAPTCHA_BACKGROUND_CLR_FROM, "WHITE");
properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789");
properties.setProperty(Constants.KAPTCHA_SESSION_CONFIG_KEY, "checkCode");
Config config = new Config(properties);
captchaProducer.setConfig(config);
return captchaProducer;
}*/
/**
* Kaptcha图形验证码工具配置类
* @author: Xiongch
* @param:
* @return: com.google.code.kaptcha.Producer
* @date: 2022/9/9 15:47
*/
@Bean
public Producer kaptchaProducer() {
// 实例一个DefaultKaptcha
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
// 创建配置对象
Properties properties = new Properties();
// 设置边框
properties.setProperty("kaptcha.border", "yes");
// 设置颜色
properties.setProperty("kaptcha.border.color", "105,179,90");
// 设置字体颜色
properties.setProperty("kaptcha.textproducer.font.color", "blue");
// 设置宽度
properties.setProperty("kaptcha.image.width", "125");
// 高度
properties.setProperty("kaptcha.image.height", "50");
// 设置session.key
properties.setProperty("kaptcha.session.key", "code");
// 设置文本长度
properties.setProperty("kaptcha.textproducer.char.length", "4");
// 设置字体
properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
// 将以上属性设置为实例一个DefaultKaptcha的属性
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
// 将defaultKaptcha返回
return defaultKaptcha;
}
}
3. 添加控制层代码
package com.example.demo;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
@Controller
public class CommonController {
@Autowired
private Producer captchaProducer;
@RequestMapping("/getCode")
public ModelAndView getKaptchaImage(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setDateHeader("Expires", 0);
// Set standard HTTP/1.1 no-cache headers.
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
// Set IE extended HTTP/1.1 no-cache headers (use addHeader).
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
// Set standard HTTP/1.0 no-cache header.
response.setHeader("Pragma", "no-cache");
response.setContentType("image/jpeg");
// create the text for the image
String capText = captchaProducer.createText();
System.out.println("******************验证码是: " + capText + "******************");
// store the text in the session
request.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
// create the image with the text
BufferedImage bi = captchaProducer.createImage(capText);
ServletOutputStream out = response.getOutputStream();
// write the data out
ImageIO.write(bi, "jpg", out);
try {
out.flush();
} finally {
out.close();
}
return null;
}
@PostMapping("/loginh")
public String loginByKaptcha(@RequestParam("verifyCode") String verifyCode, HttpSession session) {
String kaptchaCode = session.getAttribute("verifyCode") + "";
if (verifyCode.equals(kaptchaCode)) {
return "success";
}
return "false";
}
}
4. 查看生成的验证码
Hutool的实现方式简介
Hutool是一个便捷而又全面的Java工具类库,通过封装静态方法,降低学习API的成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
Hutool中的工具方法来自每个用户的不断完善,它涵盖了Java底层代码中的每个方面,既是大型项目开发中解决小问题的利器,也是小型项目中的有利助手;
Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。
一、效果展示
1.1 LineCaptcha 线段干扰
1.2 ShearCaptcha 扭曲干扰
1.3 CircleCaptcha 圆圈干扰
二、Hutool工具类实现验证码生成
2.1 引入依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.31</version>
</dependency>
2.2 核心代码
package com.example.demo;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.ShearCaptcha;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Date;
/**
* CaptchaController 验证码
* @author Administrator
* @version 2024/08/19 14:33
**/
@RestController
@RequestMapping("/captcha")
public class CaptchaController {
private static final Logger log = LoggerFactory.getLogger(CaptchaController.class);
@Autowired
private CaptchaProperties captchaProp;
/**
* 写出到流
* @param response
* @param session
*/
@RequestMapping("/get")
public void getCaptcha(HttpServletResponse response, HttpSession session) {
// 定义图形验证码的长、宽、验证码字符数、干扰元素个数
LineCaptcha captcha3 = CaptchaUtil.createLineCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
// 扭曲干扰
ShearCaptcha captcha2 = CaptchaUtil.createShearCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
// 圆圈干扰
CircleCaptcha captcha1 = CaptchaUtil.createCircleCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
//细节问题,不影响程序
//设置返回类型
response.setContentType("image/jpeg");
//静止缓存
response.setHeader("Progma", "No-cache");
try {
//图形验证码写出,可以写出到文件,也可以写出到流
captcha1.write(response.getOutputStream());
//同时将验证码内容和当前时间戳存储到Session中
//此处Session的键可以配置成常量
session.setAttribute(captchaProp.getSession().getKey(), captcha1.getCode());
session.setAttribute(captchaProp.getSession().getDate(), new Date());
log.info(captcha1.getCode());
//关流
response.getOutputStream().close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 写出到文件
*
* @param response
* @param session
* @return
*/
@RequestMapping("/getFile")
public boolean getCaptchaFile(HttpServletResponse response, HttpSession session) {
//定义图形验证码的长、宽、验证码字符数、干扰元素个数
LineCaptcha captcha3 = CaptchaUtil.createLineCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
// 扭曲干扰
ShearCaptcha captcha2 = CaptchaUtil.createShearCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
// 圆圈干扰
CircleCaptcha captcha1 = CaptchaUtil.createCircleCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
//图形验证码写出,可以写出到文件,也可以写出到流
captcha1.write("d:/shear.png");
//验证图形验证码的有效性,返回boolean值
return captcha1.verify(captcha1.getCode());
}
//验证码生效时间限制
private static final long VALID_MILLIS_TIME = 60 * 1000;
@RequestMapping("/check")
public boolean checkCaptcha(String captcha, HttpSession session) {
//保证传过来的参数是合法的
if (StringUtils.hasLength(captcha)) {
//根据配置的默认session信息获取key和date
String key = (String) session.getAttribute(captchaProp.getSession().getKey());
Date date = (Date) session.getAttribute(captchaProp.getSession().getDate());
//1.验证码正确(不区分大小写) 2.验证码还未失效
return key.equalsIgnoreCase(captcha)
&& System.currentTimeMillis() - date.getTime() < VALID_MILLIS_TIME;
}
return false;
}
}
-
利用@Autowired注解注入一个CaptchaProperties对象,这个对象将用来配置图形验证码的属性的。这是因为代码中一些属性可能会在别处使用,且它们都是固定的,例如图形验证码的长和宽,Session的字段名等。将这些属性封装在一个对象中,并通过读取配置文件的方式绑定属性值。
-
在@RequestMapping注解的getCaptcha方法中,使用Hutool包的CaptchaUtil.createLineCaptcha方法创建了一个图形验证码对象lineCaptcha,其大小由配置文件中的captchaProp.getWidth()和captchaProp.getHeight()决定。然后,将验证码内容和当前时间戳存储到HttpSession中,以便后续校验验证码的时候使用。最后,将图形验证码写出到HttpServletResponse的输出流中,以返回给前端页面显示。
-
在@RequestMapping注解的checkCaptcha方法中,首先判断传入的验证码参数是否合法。如果合法,则从HttpSession中获取存储的验证码内容和时间戳信息,并进行比较。如果验证码正确且未过期,则返回true,否则返回false。
2.3 CaptchaProperties类的实现和配置文件(.yml)信息如下:
package com.example.demo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* CaptchaProperties 验证码配置类
* @author Administrator
* @version 2024/08/19 14:33
**/
@Data
@Configuration
@ConfigurationProperties(prefix = "captcha")
public class CaptchaProperties {
// 图片宽
private Integer width;
// 图片高
private Integer height;
// 字符个数
private Integer codeCount;
// 干扰圆圈条数
private Integer circleCount;
private Session session; //使用自定义的Session类
@Data
public static class Session {
private String key;
private String date;
}
}
captcha:
width: 200
height: 60
codeCount: 4
circleCount: 100
session:
key: CAPTCHA_SESSION_KEY
date: CAPTCHA_SESSION_DATE
调用接口,如下
2.4 自定义纯数字的验证码(随机4位数字,可重复)
/**
* 写出到流,自定义纯数字的验证码
* @param response
* @param session
*/
@RequestMapping("/getCodeGenerator")
public void getCodeGenerator(HttpServletResponse response, HttpSession session) {
// 自定义纯数字的验证码(随机4位数字,可重复)
RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
// 自定义纯汉字的验证码(随机4位汉字,可重复)
RandomGenerator randomGenerator = new RandomGenerator("大家好,因基础设施故障,导致网易云音乐各端无法正常使用,我们正在加紧修复,非常抱歉。感谢大家的等待", 4);
// 定义图形验证码的长、宽、验证码字符数、干扰元素个数
LineCaptcha captcha3 = CaptchaUtil.createLineCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
// 扭曲干扰
ShearCaptcha captcha2 = CaptchaUtil.createShearCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
// 圆圈干扰
CircleCaptcha captcha1 = CaptchaUtil.createCircleCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
//细节问题,不影响程序
//设置返回类型
response.setContentType("image/jpeg");
//静止缓存
response.setHeader("Progma", "No-cache");
try {
captcha1.setGenerator(randomGenerator);
//图形验证码写出,可以写出到文件,也可以写出到流
captcha1.write(response.getOutputStream());
//同时将验证码内容和当前时间戳存储到Session中
//此处Session的键可以配置成常量
session.setAttribute(captchaProp.getSession().getKey(), captcha1.getCode());
session.setAttribute(captchaProp.getSession().getDate(), new Date());
log.info(captcha1.getCode());
//关流
response.getOutputStream().close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
2.5 自定义验证码内容为四则运算方式
/**
* 写出到流,// 自定义验证码内容为四则运算方式
* @param response
* @param session
*/
@RequestMapping("/getCodeGenerator")
public void getCodeGenerator(HttpServletResponse response, HttpSession session) {
// 定义图形验证码的长、宽、验证码字符数、干扰元素个数
LineCaptcha captcha3 = CaptchaUtil.createLineCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
// 扭曲干扰
ShearCaptcha captcha2 = CaptchaUtil.createShearCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
// 圆圈干扰
CircleCaptcha captcha1 = CaptchaUtil.createCircleCaptcha(captchaProp.getWidth(), captchaProp.getHeight(), captchaProp.getCodeCount(), captchaProp.getCircleCount());
//细节问题,不影响程序
//设置返回类型
response.setContentType("image/jpeg");
//静止缓存
response.setHeader("Progma", "No-cache");
try {
// 自定义验证码内容为四则运算方式new MathGenerator(1),1为运算位数
captcha1.setGenerator(new MathGenerator(1));
//图形验证码写出,可以写出到文件,也可以写出到流
captcha1.write(response.getOutputStream());
//同时将验证码内容和当前时间戳存储到Session中
//此处Session的键可以配置成常量
session.setAttribute(captchaProp.getSession().getKey(), captcha1.getCode());
session.setAttribute(captchaProp.getSession().getDate(), new Date());
log.info(captcha1.getCode());
//关流
response.getOutputStream().close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
验证方法
@RequestMapping("/check")
public boolean checkCaptcha(String captcha, HttpSession session) {
//保证传过来的参数是合法的
if (StringUtils.hasLength(captcha)) {
//根据配置的默认session信息获取key和date
String key = (String) session.getAttribute(captchaProp.getSession().getKey());
Date date = (Date) session.getAttribute(captchaProp.getSession().getDate());
boolean verify = new MathGenerator().verify(key, captcha);
//1.验证码正确(不区分大小写) 2.验证码还未失效
return verify
&& System.currentTimeMillis() - date.getTime() < VALID_MILLIS_TIME;
}
return false;
}