小伙伴们好,欢迎关注,一起学习,无限进步
文章目录
- 通用公共配置
- 公共依赖
- 配置 `application.yml` 文件
- 公共常量
- Redis 序列化配置
- Redis 工具类
- 通用返回结果封装
- 方法一:Graphics2D 画图实现
- 1、验证码工具类
- 2、Controller 控制层
- 方法二:kaptcha 依赖实现
- 1、验证码配置
- 2、验证码文本生成器
- 3、测试正码生成
通用公共配置
公共依赖
SpringBoot项目
<!-- redis 缓存 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><!-- pool 对象池,springboot2.x以后用得是lettuce,添加连接池依赖 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency><!-- 方法二需要用到的jar包 -->
<dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version>
</dependency>
配置 application.yml
文件
spring:# redis 配置redis:# 地址host: localhost# 端口,默认为6379port: 6379# 数据库索引database: 0# 密码password:# 连接超时时间timeout: 10slettuce:pool:# 连接池中的最小空闲连接min-idle: 0# 连接池中的最大空闲连接max-idle: 8# 连接池的最大数据库连接数max-active: 8# #连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms
公共常量
/*** 公共常量*/
public class ConstantCode {/*** 系统缓存常量*/public static final String KAPTCHA_KEY = "ss:kaptcha";public static final Long REDIS_EXP_TIME = 60 * 60L;public static final Long KAPTCHA_EXP_TIME = 60 * 3L;/*** 验证码宽高常量*/public static final Integer WIDTH = 200;public static final Integer HEIGHT = 55;/*** 图片后缀名称*/public static final String IMG_JPG = "jpg";public static final String IMG_PNG = "png";public static final String IMG_JPEG = "jpeg";}
Redis 序列化配置
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Bean@SuppressWarnings("all")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {// 为了自己开发方便,一般直接使用 <String, Object>RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();template.setConnectionFactory(factory);// Json序列化配置Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// String 的序列化StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);// hash的key也采用String的序列化方式template.setHashKeySerializer(stringRedisSerializer);// value序列化方式采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的value序列化方式采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}
}
Redis 工具类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.util.Collection;
import java.util.concurrent.TimeUnit;/*** Redis工具类*/
@Component
@SuppressWarnings("all")
public class RedisUtils {/*** 日志对象*/private static Logger logger = LoggerFactory.getLogger(RedisUtils.class);@Autowiredprivate RedisTemplate redisTemplate;/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值* @param timeout 时间* @param timeUnit 时间颗粒度*/public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T getCacheObject(final String key) {ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 获得缓存的基本对象列表** @param pattern 字符串前缀* @return 对象列表*/public Collection<String> keys(final String pattern) {return redisTemplate.keys(pattern);}/*** 删除集合对象** @param collection 多个对象* @return*/public long deleteObject(final Collection collection) {return redisTemplate.delete(collection);}/*** 指定缓存失效时间** @param key 键* @param time 时间(秒)* @return*/public boolean expire(String key, long time) {try {if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {logger.error("RedisUtils expire(String key,long time) failure." + e.getMessage());return false;}}/*** 根据key 获取过期时间** @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {logger.error("RedisUtils hasKey(String key) failure." + e.getMessage());return false;}}/*** 删除缓存** @param key 可以传一个值 或多个*/public void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(key));}}}//============================String=============================/*** 普通缓存获取** @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** @param key 键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {logger.error("RedisUtils set(String key,Object value) failure." + e.getMessage());return false;}}/*** 普通缓存放入并设置时间** @param key 键* @param value 值* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {logger.error("RedisUtils set(String key,Object value,long time) failure." + e.getMessage());return false;}}
}
通用返回结果封装
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import java.util.HashMap;/*** 操作消息提醒*/
public class Result extends HashMap<String, Object> {private static final long serialVersionUID = 1L;/*** 状态码*/public static final String CODE_TAG = "code";/*** 返回内容*/public static final String MSG_TAG = "msg";/*** 数据对象*/public static final String DATA_TAG = "data";/*** 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。*/public Result() {}/*** 初始化一个新创建的 AjaxResult 对象** @param code 状态码* @param msg 返回内容*/public Result(int code, String msg) {super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}/*** 初始化一个新创建的 AjaxResult 对象** @param code 状态码* @param msg 返回内容* @param data 数据对象*/public Result(int code, String msg, Object data) {super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if (!StringUtils.isEmpty(data)) {super.put(DATA_TAG, data);}}/*** 返回成功消息** @return 成功消息*/public static Result success() {return Result.success("操作成功");}/*** 返回成功数据** @return 成功消息*/public static Result success(Object data) {return Result.success("操作成功", data);}/*** 返回成功消息** @param msg 返回内容* @return 成功消息*/public static Result success(String msg) {return Result.success(msg, null);}/*** 返回成功消息** @param msg 返回内容* @param data 数据对象* @return 成功消息*/public static Result success(String msg, Object data) {return new Result(HttpStatus.OK.value(), msg, data);}/*** 返回错误消息** @return*/public static Result error() {return Result.error("操作失败");}/*** 返回错误消息** @param msg 返回内容* @return 警告消息*/public static Result error(String msg) {return Result.error(msg, null);}/*** 返回错误消息** @param msg 返回内容* @param data 数据对象* @return 警告消息*/public static Result error(String msg, Object data) {return new Result(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, data);}/*** 返回错误消息** @param code 状态码* @param msg 返回内容* @return 警告消息*/public static Result error(int code, String msg) {return new Result(code, msg, null);}/*** 方便链式调用** @param key 键* @param value 值* @return 数据对象*/@Overridepublic Result put(String key, Object value) {super.put(key, value);return this;}
}
方法一:Graphics2D 画图实现
使用 jdk 画图 Graphics2D 生成验证码
1、验证码工具类
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;/*** 验证码工具类*/
public class VerifyCodeUtils {/*** 传入BufferedImage对象,并将生成好的验证码保存到BufferedImage中*/public static String drawRandomText(BufferedImage bufferedImage, int width, int height) {Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();// 验证码背景色graphics.setColor(new Color(255, 255, 255));// 填充线条背景graphics.fillRect(0, 0, width, height);graphics.setFont(new Font("宋体,楷体,微软雅黑", Font.BOLD, 35));// 数字和字母的组合String baseNumLetter = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";StringBuilder builder = new StringBuilder();// 旋转原点的 x 坐标int x = 40;String ch;Random random = new Random();for (int i = 0; i < 4; i++) {graphics.setColor(getRandomColor());//设置字体旋转角度,角度小于30度int degree = random.nextInt() % 30;int dot = random.nextInt(baseNumLetter.length());ch = baseNumLetter.charAt(dot) + "";builder.append(ch);//正向旋转graphics.rotate(degree * Math.PI / 180, x, 45);graphics.drawString(ch, x, 45);//反向旋转graphics.rotate(-degree * Math.PI / 180, x, 45);// 字母间距记录x += 35;}// 画干扰线for (int i = 0; i < 6; i++) {// 设置随机颜色graphics.setColor(getRandomColor());// 随机画线graphics.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width), random.nextInt(height));}// 添加噪点for (int i = 0; i < 30; i++) {int x1 = random.nextInt(width);int y1 = random.nextInt(height);graphics.setColor(getRandomColor());graphics.fillRect(x1, y1, 2, 2);}return builder.toString();}/*** 随机取色*/private static Color getRandomColor() {Random ran = new Random();return new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));}
}
2、Controller 控制层
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Base64;
import java.util.UUID;@RestController
public class VerificationController {@Autowiredprivate RedisUtils redisUtils;/*** 验证码生成工具,返回图片信息* 适合学习练手使用,redis 呢能存在一个* @param response* @throws IOException*/@GetMapping("/getImageCode")public void getImageCode(HttpServletResponse response) throws IOException {// 禁止缓存response.setDateHeader("Expires", 0);response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");response.addHeader("Cache-Control", "post-check=0, pre-check=0");response.setHeader("Pragma", "no-cache");// 设置响应格式为png图片response.setContentType("image/png");// 生成图片验证码BufferedImage image = new BufferedImage(ConstantCode.WIDTH, ConstantCode.HEIGHT, BufferedImage.TYPE_INT_RGB);String randomText = VerifyCodeUtils.drawRandomText(image, ConstantCode.WIDTH, ConstantCode.HEIGHT);// 存入redisredisUtils.set(ConstantCode.KAPTCHA_KEY, randomText, ConstantCode.KAPTCHA_EXP_TIME);ServletOutputStream out = response.getOutputStream();ImageIO.write(image, ConstantCode.IMG_JPG, out);out.flush();out.close();}/*** 验证码 Base64* 可生成多个* @return*/@GetMapping("/getCaptchaInfo")public Result getCaptchaInfo() {Result success = Result.success();// 生成图片验证码BufferedImage image = new BufferedImage(ConstantCode.WIDTH, ConstantCode.HEIGHT, BufferedImage.TYPE_INT_RGB);String uuid = UUID.randomUUID().toString().replace("-", "");// 验证码keyString verifyKey = ConstantCode.KAPTCHA_KEY + uuid;String randomText = VerifyCodeUtils.drawRandomText(image, ConstantCode.WIDTH, ConstantCode.HEIGHT);// 存入redisredisUtils.set(verifyKey, randomText, ConstantCode.KAPTCHA_EXP_TIME);// 转换流信息写出FastByteArrayOutputStream os = new FastByteArrayOutputStream();try {ImageIO.write(image, "jpg", os);} catch (IOException e) {return Result.error(e.getMessage());}success.put("uuid", uuid);success.put("img", Base64.getEncoder().encodeToString(os.toByteArray()));return success;}
}
方法二:kaptcha 依赖实现
1、验证码配置
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
import static com.google.code.kaptcha.Constants.*;/*** 验证码配置*/
@Configuration
public class CaptchaConfig {@Bean(name = "captchaProducer")public DefaultKaptcha getKaptchaBean() {DefaultKaptcha defaultKaptcha = new DefaultKaptcha();Properties properties = new Properties();// 是否有边框 默认为true 我们可以自己设置yes,noproperties.setProperty(KAPTCHA_BORDER, "yes");// 验证码文本字符颜色 默认为Color.BLACKproperties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");// 验证码图片宽度 默认为200properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");// 验证码图片高度 默认为50properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");// 验证码文本字符大小 默认为40properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");// KAPTCHA_SESSION_KEYproperties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");// 验证码文本字符长度 默认为5properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpyproperties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");Config config = new Config(properties);defaultKaptcha.setConfig(config);return defaultKaptcha;}@Bean(name = "captchaProducerMath")public DefaultKaptcha getKaptchaBeanMath() {DefaultKaptcha defaultKaptcha = new DefaultKaptcha();Properties properties = new Properties();// 是否有边框 默认为true 我们可以自己设置yes,noproperties.setProperty(KAPTCHA_BORDER, "yes");// 边框颜色 默认为Color.BLACKproperties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");// 验证码文本字符颜色 默认为Color.BLACKproperties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");// 验证码图片宽度 默认为200properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");// 验证码图片高度 默认为50properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");// 验证码文本字符大小 默认为40properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");// KAPTCHA_SESSION_KEYproperties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");// 验证码文本生成器 注意:KaptchaTextCreator的路径properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.test.demo.KaptchaTextCreator");// 验证码文本字符间距 默认为2properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");// 验证码文本字符长度 默认为5properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");// 验证码噪点颜色 默认为Color.BLACKproperties.setProperty(KAPTCHA_NOISE_COLOR, "white");// 干扰实现类properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpyproperties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");Config config = new Config(properties);defaultKaptcha.setConfig(config);return defaultKaptcha;}
}
2、验证码文本生成器
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
import java.util.Random;/*** 验证码文本生成器*/
public class KaptchaTextCreator extends DefaultTextCreator {private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");@Overridepublic String getText() {Integer result = 0;Random random = new Random();int x = random.nextInt(10);int y = random.nextInt(10);StringBuilder suChinese = new StringBuilder();int randomoperands = (int) Math.round(Math.random() * 2);if (randomoperands == 0) {result = x * y;suChinese.append(CNUMBERS[x]);suChinese.append("*");suChinese.append(CNUMBERS[y]);} else if (randomoperands == 1) {if (!(x == 0) && y % x == 0) {result = y / x;suChinese.append(CNUMBERS[y]);suChinese.append("/");suChinese.append(CNUMBERS[x]);} else {result = x + y;suChinese.append(CNUMBERS[x]);suChinese.append("+");suChinese.append(CNUMBERS[y]);}} else if (randomoperands == 2) {if (x >= y) {result = x - y;suChinese.append(CNUMBERS[x]);suChinese.append("-");suChinese.append(CNUMBERS[y]);} else {result = y - x;suChinese.append(CNUMBERS[y]);suChinese.append("-");suChinese.append(CNUMBERS[x]);}} else {result = x + y;suChinese.append(CNUMBERS[x]);suChinese.append("+");suChinese.append(CNUMBERS[y]);}suChinese.append("=?@" + result);return suChinese.toString();}
}
3、测试正码生成
import com.google.code.kaptcha.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Base64;
import java.util.UUID;/*** 验证码操作处理*/
@RestController
public class CaptchaController {@Resource(name = "captchaProducer")private Producer captchaProducer;@Resource(name = "captchaProducerMath")private Producer captchaProducerMath;@Autowiredprivate RedisUtils redisUtils;/*** 文本验证码** @return*/@GetMapping("/captchaCharImage")public Result getCharCode() {Result ajax = Result.success();// 生成验证码String capText = captchaProducerMath.createText();String capStr = capText.substring(0, capText.lastIndexOf("@"));String code = capText.substring(capText.lastIndexOf("@") + 1);BufferedImage image = captchaProducerMath.createImage(capStr);// 保存验证码信息String uuid = UUID.randomUUID().toString().replace("-", "");String verifyKey = ConstantCode.KAPTCHA_KEY + uuid;redisUtils.set(verifyKey, code, ConstantCode.KAPTCHA_EXP_TIME);// 转换流信息写出FastByteArrayOutputStream os = new FastByteArrayOutputStream();try {ImageIO.write(image, "jpg", os);} catch (IOException e) {return Result.error(e.getMessage());}ajax.put("uuid", uuid);ajax.put("img", Base64.getEncoder().encodeToString(os.toByteArray()));return ajax;}/*** 生成验证码*/@GetMapping("/captchaMathImage")public Result getMathCode() {Result ajax = Result.success();// 生成验证码String capText = captchaProducer.createText();BufferedImage image = captchaProducerMath.createImage(capText);// 保存验证码信息String uuid = UUID.randomUUID().toString().replace("-", "");String verifyKey = ConstantCode.KAPTCHA_KEY + uuid;redisUtils.set(verifyKey, capText, ConstantCode.KAPTCHA_EXP_TIME);// 转换流信息写出FastByteArrayOutputStream os = new FastByteArrayOutputStream();try {ImageIO.write(image, "jpg", os);} catch (IOException e) {return Result.error(e.getMessage());}ajax.put("uuid", uuid);ajax.put("img", Base64.getEncoder().encodeToString(os.toByteArray()));return ajax;}
}