引入依赖包
XML
<!-- 图形验证码 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
配置DefaultKaptcha类
类方法配置了验证码的生成格式和规则,并返回一个DefaultKaptcha对象并注入到Spring中
java
package com.dream.datacenter.config;
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;
/**
* Created with IntelliJ IDEA
* 图形验证码属性配置
* @Author: Mr.HPC
* @Date: 2022/1/6 23:08
* @Version 1.0
*/
@Component
public class CodeImgConfig {
@Bean
public DefaultKaptcha getDefaultKaptcha() {
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 图片边框
properties.setProperty("kaptcha.border", "no");
// 边框颜色
properties.setProperty("kaptcha.border.color", "black");
//边框厚度
properties.setProperty("kaptcha.border.thickness", "1");
// 图片宽
properties.setProperty("kaptcha.image.width", "200");
// 图片高
properties.setProperty("kaptcha.image.height", "50");
//图片实现类
properties.setProperty("kaptcha.producer.impl", "com.google.code.kaptcha.impl.DefaultKaptcha");
//文本实现类
properties.setProperty("kaptcha.textproducer.impl", "com.google.code.kaptcha.text.impl.DefaultTextCreator");
//文本集合,验证码值从此集合中获取
properties.setProperty("kaptcha.textproducer.char.string", "01234567890");
//验证码长度
properties.setProperty("kaptcha.textproducer.char.length", "4");
//字体
properties.setProperty("kaptcha.textproducer.font.names", "宋体");
//字体颜色
properties.setProperty("kaptcha.textproducer.font.color", "black");
//文字间隔
properties.setProperty("kaptcha.textproducer.char.space", "5");
//干扰实现类
properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");
//干扰颜色
properties.setProperty("kaptcha.noise.color", "blue");
//干扰图片样式
properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
//背景实现类
properties.setProperty("kaptcha.background.impl", "com.google.code.kaptcha.impl.DefaultBackground");
//背景颜色渐变,结束颜色
properties.setProperty("kaptcha.background.clear.to", "white");
//文字渲染器
properties.setProperty("kaptcha.word.impl", "com.google.code.kaptcha.text.impl.DefaultWordRenderer");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
Controller 请求方法
java
package com.dream.datacenter.controller;
import com.dream.datacenter.shiro.JWTUtil;
import com.dream.datacenter.utils.ImageUtil;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/test")
public class testController {
public static final String VERIFICATION_CODE = "verificationCode_";
@Autowired
public RedisTemplate redisTemplate;
@Resource
private DefaultKaptcha captcha;
/**
* 第一种方式
* 使用DefaultKaptcha生成登录验证码图片
* 转成base64编码的字符串输出到前端
* 实际项目这里还要加入JWT令牌校验和redis缓存
*/
@RequestMapping(value = {"/loginValidateCode"})
public String loginValidateCode(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");
// return a jpeg
response.setContentType("image/jpeg");
// 生成图片验证码内容
String capText = captcha.createText();
// 获取用户的jwt令牌
String userVerificationJwt = request.getHeader(JWTUtil.JWT_VERIFICATION_KEY);
//验证码令牌
Claims claims = JWTUtil.validateJwtToken(userVerificationJwt);
if(claims == null){
//如果用户令牌过期那么对应存放在redis中的数据也要清空
if(!org.apache.commons.lang3.StringUtils.isEmpty(userVerificationJwt)){
redisTemplate.expire(VERIFICATION_CODE + userVerificationJwt, 1, TimeUnit.DAYS);
}
//重新生成用户令牌
userVerificationJwt = JWTUtil.createJwt(new HashMap<String, Object>() ,JWTUtil.EXPIRE_TIME);
//将jwt令牌放入 response head中
response.setHeader(JWTUtil.JWT_VERIFICATION_KEY, userVerificationJwt);
}
//刷新缓存,更新验证码
redisTemplate.opsForValue().set(VERIFICATION_CODE + userVerificationJwt , capText,60, TimeUnit.SECONDS);
System.out.println(capText);
// ImageUtil.validateCode(response,captcha,capText);
//生成图片
String code = "data:image/png;base64," + ImageUtil.validateCode(captcha,capText);
return code;
}
/**
* 第二种方式
* 使用Graphics绘图来定制格式,生成验证码
* 转成base64编码的字符串输出到前端
*/
@RequestMapping(value = {"/loginValidateCode1"})
public String loginValidateCode1(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");
// return a jpeg
response.setContentType("image/png");
String str = captcha.createText();//使用DefaultKaptcha规则生成随机码
//生成图片
String code = "data:image/png;base64," + ImageUtil.createImageWithVerifyCode(str,200,50);
return code;
}
/**
* 第三种方式
* 使用DefaultKaptcha生成登录验证码图片
* 并用文件流方式输出到前端
*/
@RequestMapping(value = {"/loginValidateCode2"})
public void loginValidateCode2(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");
// return a jpeg
response.setContentType("image/png");
ImageUtil.validateCode(response,captcha,captcha.createText());
}
}
ImageUtil公共类
java
package com.dream.datacenter.utils;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import java.util.Base64;
import java.util.Base64.Encoder;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Random;
/**
* Created with IntelliJ IDEA
*
* @Author: Mr.HPC
* @Date: 2022/1/6 23:19
* @Version 1.0
* 验证码工具类
*/
public class ImageUtil {
/**
* 第一种,用于IO输出
* @param captchaProducer 生成图片方法类
* @throws Exception
*/
public static void validateCode(HttpServletResponse response,
DefaultKaptcha captchaProducer,String capText) throws Exception{
// create the image with the text
BufferedImage bi = captchaProducer.createImage(capText);
ServletOutputStream out = response.getOutputStream();
// write the data out
ImageIO.write(bi, "JPEG", out);
try {
out.flush();
} finally {
out.close();
}
}
/**
* 第二种
* 生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性
* @param captchaProducer 生成图片方法类
* @throws Exception
*/
public static String validateCode(DefaultKaptcha captchaProducer,String capText) throws Exception{
// create the image with the text
BufferedImage bi = captchaProducer.createImage(capText);
return returnPicBase64(bi);
}
/**
* 第三种,生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性
* @param word 要生存的验证码随机字符串
* @param width 图片宽度
* @param height 图片高度
* @return base64 格式生成的验证码图片
* @throws IOException
*/
public static String createImageWithVerifyCode(String word, int width, int height) throws IOException {
String png_base64="";
//绘制内存中的图片
BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//得到画图对象
Graphics graphics = bufferedImage.getGraphics();
//绘制图片前指定一个颜色
graphics.setColor(getRandColor(160,200));
graphics.fillRect(0,0,width,height);
//绘制边框
graphics.setColor(Color.white);
graphics.drawRect(0, 0, width - 1, height - 1);
// 步骤四 四个随机数字
Graphics2D graphics2d = (Graphics2D) graphics;
graphics2d.setFont(new Font("宋体", Font.BOLD, 18));
Random random = new Random();
// 定义x坐标
int x = 10;
for (int i = 0; i < word.length(); i++) {
// 随机颜色
graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
// 旋转 -30 --- 30度
int jiaodu = random.nextInt(60) - 30;
// 换算弧度
double theta = jiaodu * Math.PI / 180;
// 获得字母数字
char c = word.charAt(i);
//将c 输出到图片
graphics2d.rotate(theta, x, 20);
graphics2d.drawString(String.valueOf(c), x, 20);
graphics2d.rotate(-theta, x, 20);
x += 30;
}
// 绘制干扰线
graphics.setColor(getRandColor(160, 200));
int x1;
int x2;
int y1;
int y2;
for (int i = 0; i < 30; i++) {
x1 = random.nextInt(width);
x2 = random.nextInt(12);
y1 = random.nextInt(height);
y2 = random.nextInt(12);
graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
}
graphics.dispose();// 释放资源
return returnPicBase64(bufferedImage);
}
/**
* 将图片字节数组码转为base64编码
* @param bi
* @return
* @throws IOException
*/
private static String returnPicBase64(BufferedImage bi) throws IOException {
String png_base64;
ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
ImageIO.write(bi, "png", baos);//写入流中
byte[] bytes = baos.toByteArray();//转换成字节
// BASE64Encoder encoder = new BASE64Encoder();
// png_base64 = encoder.encodeBuffer(bytes).trim();
Encoder encoder = Base64.getEncoder();
png_base64 = encoder.encodeToString(bytes).trim();
png_base64 = png_base64.replaceAll("\n", "").replaceAll("\r", "");//删除 \r\n
return png_base64;
}
/**设置随机颜色*/
private static Color getRandColor(int fc, int bc) {
// 取其随机颜色
Random random = new Random();
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
}
前端代码的调用
test.js 文件
javascript
// 图形校验码接口
export const codeImgUrl = "/api/test/loginValidateCode";
export const codeImgUrl1 = "/api/test/loginValidateCode1";
export const codeImgUrl2 = "/api/test/loginValidateCode2";
yzm.vue 文件,其中 refreshCode2() 方法的 this.ruleForm.codeimg2 = window.URL.createObjectURL(blob); 是将图片io流转成浏览器可以识别的URL
html
<template>
<div class="login-wrap">
<el-form label-position="left" :model="ruleForm" :rules="rules" ref="ruleForm" label-width="0px" class="demo-ruleForm login-container">
<el-row>
<el-col :span="4" class="code-box">
<el-text>生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性</el-text>
<img :src="ruleForm.codeimg" alt="验证码" class="codeimg" @click="refreshCode()">
</el-col>
<el-col :span="4" class="code-box">
<el-text>生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性</el-text>
<img :src="ruleForm.codeimg1" alt="验证码" class="codeimg" @click="refreshCode1()">
</el-col>
<el-col :span="4" class="code-box">
<el-text>生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性</el-text>
<img :src="ruleForm.codeimg2" alt="验证码" class="codeimg" @click="refreshCode2()">
</el-col>
</el-row>
</el-form>
</div>
</template>
<script type="text/ecmascript-6">
import { codeImgUrl,codeImgUrl1,codeImgUrl2 } from '../../api/test';
import md5 from 'js-md5';
import axios from 'axios';
export default {
name: 'yzm',
data() {
return {
ruleForm: {
code: '',
codeimg: '',
codeimg1: '',
codeimg2: ''
},
//rules前端验证
rules: {
}
}
},
// 创建完毕状态(里面是操作)
created() {
// 获取图形验证码
this.refreshCode()
this.refreshCode1()
this.refreshCode2()
},
methods: {
//获取图形验证码
refreshCode() {
// this.ruleForm.codeimg = codeImgUrl+"?t="+Date.now();
let url = codeImgUrl+"?t="+Date.now();
axios.get(url).then(res => {
this.ruleForm.codeimg = res.data
}).catch(resp => {
console.log(resp);
});
},
refreshCode1() {
// this.ruleForm.codeimg = codeImgUrl+"?t="+Date.now();
let url = codeImgUrl1+"?t="+Date.now();
axios.get(url).then(res => {
this.ruleForm.codeimg1 = res.data
}).catch(resp => {
console.log(resp);
});
},
refreshCode2() {
// this.ruleForm.codeimg = codeImgUrl+"?t="+Date.now();
let url = codeImgUrl2+"?t="+Date.now();
axios.request({
url: url,
responseType: 'blob',
method: 'get'
}).then(res => {
let blob = new Blob([res.data], {
type: "image/png"
});
this.ruleForm.codeimg2 = window.URL.createObjectURL(blob);
}), (error) => {
this.$message({
type: 'warning',
message: '系统错误,请联系管理员!'
});
}
},
}
}
</script>
<style scoped>
</style>