生成图片验证码-Google Kaptcha

CaptchaImage生成 验证码 图片

captchaProducerMath.createText() 类似

captchaProducer.createText() 混合带字符的char如下

从若依学的,先看他的引用方式


package com.ruoyi.web.controller.common;import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
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 com.google.code.kaptcha.Producer;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.service.ISysConfigService;/*** 验证码操作处理* * @author ruoyi*/
@RestController
public class CaptchaController
{@Resource(name = "captchaProducer")private Producer captchaProducer;@Resource(name = "captchaProducerMath")private Producer captchaProducerMath;@Autowiredprivate RedisCache redisCache;@Autowiredprivate ISysConfigService configService;/*** 生成验证码*/@GetMapping("/captchaImage")public AjaxResult getCode(HttpServletResponse response) throws IOException{AjaxResult ajax = AjaxResult.success();boolean captchaEnabled = configService.selectCaptchaEnabled();ajax.put("captchaEnabled", captchaEnabled);if (!captchaEnabled){return ajax;}// 保存验证码信息String uuid = IdUtils.simpleUUID();String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;String capStr = null, code = null;BufferedImage image = null;// 生成验证码String captchaType = RuoYiConfig.getCaptchaType();if ("math".equals(captchaType)){String capText = captchaProducerMath.createText();capStr = capText.substring(0, capText.lastIndexOf("@"));code = capText.substring(capText.lastIndexOf("@") + 1);image = captchaProducerMath.createImage(capStr);}else if ("char".equals(captchaType)){capStr = code = captchaProducer.createText();image = captchaProducer.createImage(capStr);}redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);// 转换流信息写出FastByteArrayOutputStream os = new FastByteArrayOutputStream();try{ImageIO.write(image, "jpg", os);}catch (IOException e){return AjaxResult.error(e.getMessage());}ajax.put("uuid", uuid);ajax.put("img", Base64.encode(os.toByteArray()));return ajax;}
}

前端直接 传HttpServletResponse 访问url调用 ruoyi-ui/src/api/login.js


import request from '@/utils/request'
...
// 获取验证码
export function getCodeImg() {return request({url: '/captchaImage',headers: {isToken: false},method: 'get',timeout: 20000})
}

ruoyi-ui/src/views/login.vue


<template><div class="login"><el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"><h3 class="title">若依后台管理系统</h3><el-form-item prop="username"><el-inputv-model="loginForm.username"type="text"auto-complete="off"placeholder="账号"><svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="loginForm.password"type="password"auto-complete="off"placeholder="密码"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="code" v-if="captchaEnabled"><el-inputv-model="loginForm.code"auto-complete="off"placeholder="验证码"style="width: 63%"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /></el-input><div class="login-code"><img :src="codeUrl" @click="getCode" class="login-code-img"/></div></el-form-item><el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox><el-form-item style="width:100%;"><el-button:loading="loading"size="medium"type="primary"style="width:100%;"@click.native.prevent="handleLogin"><span v-if="!loading">登 录</span><span v-else>登 录 中...</span></el-button><div style="float: right;" v-if="register"><router-link class="link-type" :to="'/register'">立即注册</router-link></div></el-form-item></el-form><!--  底部  --><div class="el-login-footer"><span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span></div></div>
</template><script>
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt'export default {name: "Login",data() {return {codeUrl: "",loginForm: {username: "admin",password: "admin123",rememberMe: false,code: "",uuid: ""},loginRules: {username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],code: [{ required: true, trigger: "change", message: "请输入验证码" }]},loading: false,// 验证码开关captchaEnabled: true,// 注册开关register: false,redirect: undefined};},watch: {$route: {handler: function(route) {this.redirect = route.query && route.query.redirect;},immediate: true}},created() {this.getCode();this.getCookie();},methods: {getCode() {getCodeImg().then(res => {this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled;if (this.captchaEnabled) {this.codeUrl = "data:image/gif;base64," + res.img;this.loginForm.uuid = res.uuid;}});},getCookie() {const username = Cookies.get("username");const password = Cookies.get("password");const rememberMe = Cookies.get('rememberMe')this.loginForm = {username: username === undefined ? this.loginForm.username : username,password: password === undefined ? this.loginForm.password : decrypt(password),rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)};},handleLogin() {this.$refs.loginForm.validate(valid => {if (valid) {this.loading = true;if (this.loginForm.rememberMe) {Cookies.set("username", this.loginForm.username, { expires: 30 });Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });} else {Cookies.remove("username");Cookies.remove("password");Cookies.remove('rememberMe');}this.$store.dispatch("Login", this.loginForm).then(() => {this.$router.push({ path: this.redirect || "/" }).catch(()=>{});}).catch(() => {this.loading = false;if (this.captchaEnabled) {this.getCode();}});}});}}
};
</script><style rel="stylesheet/scss" lang="scss">
.login {display: flex;justify-content: center;align-items: center;height: 100%;background-image: url("../assets/images/login-background.jpg");background-size: cover;
}
.title {margin: 0px auto 30px auto;text-align: center;color: #707070;
}.login-form {border-radius: 6px;background: #ffffff;width: 400px;padding: 25px 25px 5px 25px;.el-input {height: 38px;input {height: 38px;}}.input-icon {height: 39px;width: 14px;margin-left: 2px;}
}
.login-tip {font-size: 13px;text-align: center;color: #bfbfbf;
}
.login-code {width: 33%;height: 38px;float: right;img {cursor: pointer;vertical-align: middle;}
}
.el-login-footer {height: 40px;line-height: 40px;position: fixed;bottom: 0;width: 100%;text-align: center;color: #fff;font-family: Arial;font-size: 12px;letter-spacing: 1px;
}
.login-code-img {height: 38px;
}
</style>

login.vue与验证码相关之处

<template><div class="login"><el-form-item prop="code" v-if="captchaEnabled"><el-inputv-model="loginForm.code"auto-complete="off"placeholder="验证码"style="width: 63%"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /></el-input><div class="login-code"><img :src="codeUrl" @click="getCode" class="login-code-img"/></div></el-form-item><el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox></el-form></div>
</template><script>
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt'export default {name: "Login",data() {return {codeUrl: "",loginForm: {username: "admin",password: "admin123",rememberMe: false,code: "",uuid: ""},loginRules: {username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],code: [{ required: true, trigger: "change", message: "请输入验证码" }]},loading: false,// 验证码开关captchaEnabled: true,// 注册开关register: false,redirect: undefined};},created() {this.getCode();this.getCookie();},methods: {getCode() {getCodeImg().then(res => {this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled;if (this.captchaEnabled) {this.codeUrl = "data:image/gif;base64," + res.img;this.loginForm.uuid = res.uuid;}});},
</script><style rel="stylesheet/scss" lang="scss">
</style>

this.codeUrl = "data:image/gif;base64," + res.img;

加了个前缀 生成验证码图片网址输入:

data:image/gif;base64,+下面response的img

即可得到生成的图片

发送的请求:http://localhost/dev-api/captchaImage

返回的response:


{"msg":"操作成功","img":"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","code":200,"captchaEnabled":true,"uuid":"6e0305699f91421dab8baa377347bd10"}

集成步骤如下:

随便用的瑞吉外卖集成,因为瑞吉本来就需要redis,就没删redis;

任意一个由pom.XMl的项目其实都可以

pom:

<!-- 图片验证码 -->
<dependency><groupId>pro.fessional</groupId><artifactId>kaptcha</artifactId><version>2.3.3</version>
</dependency>
  1. 项目结构

package自己起名字就行

2、具体编码6个文件的内容;其它五个都是为了服务CaptchaController

CaptchaController

package com.it...;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.concurrent.TimeUnit;/*** 验证码操作处理* * @author ruoyi*/
@RestController
public class CaptchaController
{@Resource(name = "captchaProducer")private Producer captchaProducer;@Resource(name = "captchaProducerMath")private Producer captchaProducerMath;@Autowiredprivate RedisCache redisCache;/*** 验证码 redis key*/public static final String CAPTCHA_CODE_KEY = "captcha_codes:";/*** 验证码有效期(分钟)*/public static final Integer CAPTCHA_EXPIRATION = 2;/*** 生成验证码*/@GetMapping("/captchaImage")public  String getCode() throws IOException{// 保存验证码信息String uuid = IdUtils.simpleUUID();String verifyKey = CAPTCHA_CODE_KEY + uuid;String capStr = null, code = null;BufferedImage image = null;// 生成验证码String captchaType = "math";String captchaType2 = "char";if ("math".equals(captchaType2)){String capText = captchaProducerMath.createText();capStr = capText.substring(0, capText.lastIndexOf("@"));code = capText.substring(capText.lastIndexOf("@") + 1);image = captchaProducerMath.createImage(capStr);}else if ("char".equals(captchaType2)){capStr = code = captchaProducer.createText();image = captchaProducer.createImage(capStr);}redisCache.setCacheObject(verifyKey, code, CAPTCHA_EXPIRATION, TimeUnit.MINUTES);// 转换流信息写出FastByteArrayOutputStream os = new FastByteArrayOutputStream();try{ImageIO.write(image, "jpg", os);}catch (IOException e){System.out.println("验证码生成出错:"+e);}System.out.println("img:*********"+ Base64.encode(os.toByteArray()));return  Base64.encode(os.toByteArray());}
}
Base64

package com.it;/*** Base64工具类* * @author ruoyi*/
public final class Base64
{static private final int     BASELENGTH           = 128;static private final int     LOOKUPLENGTH         = 64;static private final int     TWENTYFOURBITGROUP   = 24;static private final int     EIGHTBIT             = 8;static private final int     SIXTEENBIT           = 16;static private final int     FOURBYTE             = 4;static private final int     SIGN                 = -128;static private final char    PAD                  = '=';static final private byte[]  base64Alphabet       = new byte[BASELENGTH];static final private char[]  lookUpBase64Alphabet = new char[LOOKUPLENGTH];static{for (int i = 0; i < BASELENGTH; ++i){base64Alphabet[i] = -1;}for (int i = 'Z'; i >= 'A'; i--){base64Alphabet[i] = (byte) (i - 'A');}for (int i = 'z'; i >= 'a'; i--){base64Alphabet[i] = (byte) (i - 'a' + 26);}for (int i = '9'; i >= '0'; i--){base64Alphabet[i] = (byte) (i - '0' + 52);}base64Alphabet['+'] = 62;base64Alphabet['/'] = 63;for (int i = 0; i <= 25; i++){lookUpBase64Alphabet[i] = (char) ('A' + i);}for (int i = 26, j = 0; i <= 51; i++, j++){lookUpBase64Alphabet[i] = (char) ('a' + j);}for (int i = 52, j = 0; i <= 61; i++, j++){lookUpBase64Alphabet[i] = (char) ('0' + j);}lookUpBase64Alphabet[62] = (char) '+';lookUpBase64Alphabet[63] = (char) '/';}private static boolean isWhiteSpace(char octect){return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);}private static boolean isPad(char octect){return (octect == PAD);}private static boolean isData(char octect){return (octect < BASELENGTH && base64Alphabet[octect] != -1);}/*** Encodes hex octects into Base64** @param binaryData Array containing binaryData* @return Encoded Base64 array*/public static String encode(byte[] binaryData){if (binaryData == null){return null;}int lengthDataBits = binaryData.length * EIGHTBIT;if (lengthDataBits == 0){return "";}int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;char encodedData[] = null;encodedData = new char[numberQuartet * 4];byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;int encodedIndex = 0;int dataIndex = 0;for (int i = 0; i < numberTriplets; i++){b1 = binaryData[dataIndex++];b2 = binaryData[dataIndex++];b3 = binaryData[dataIndex++];l = (byte) (b2 & 0x0f);k = (byte) (b1 & 0x03);byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];}// form integral number of 6-bit groupsif (fewerThan24bits == EIGHTBIT){b1 = binaryData[dataIndex];k = (byte) (b1 & 0x03);byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];encodedData[encodedIndex++] = PAD;encodedData[encodedIndex++] = PAD;}else if (fewerThan24bits == SIXTEENBIT){b1 = binaryData[dataIndex];b2 = binaryData[dataIndex + 1];l = (byte) (b2 & 0x0f);k = (byte) (b1 & 0x03);byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];encodedData[encodedIndex++] = PAD;}return new String(encodedData);}/*** Decodes Base64 data into octects** @param encoded string containing Base64 data* @return Array containind decoded data.*/public static byte[] decode(String encoded){if (encoded == null){return null;}char[] base64Data = encoded.toCharArray();// remove white spacesint len = removeWhiteSpace(base64Data);if (len % FOURBYTE != 0){return null;// should be divisible by four}int numberQuadruple = (len / FOURBYTE);if (numberQuadruple == 0){return new byte[0];}byte decodedData[] = null;byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;char d1 = 0, d2 = 0, d3 = 0, d4 = 0;int i = 0;int encodedIndex = 0;int dataIndex = 0;decodedData = new byte[(numberQuadruple) * 3];for (; i < numberQuadruple - 1; i++){if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))|| !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))){return null;} // if found "no data" just return nullb1 = base64Alphabet[d1];b2 = base64Alphabet[d2];b3 = base64Alphabet[d3];b4 = base64Alphabet[d4];decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);}if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))){return null;// if found "no data" just return null}b1 = base64Alphabet[d1];b2 = base64Alphabet[d2];d3 = base64Data[dataIndex++];d4 = base64Data[dataIndex++];if (!isData((d3)) || !isData((d4))){// Check if they are PAD charactersif (isPad(d3) && isPad(d4)){if ((b2 & 0xf) != 0)// last 4 bits should be zero{return null;}byte[] tmp = new byte[i * 3 + 1];System.arraycopy(decodedData, 0, tmp, 0, i * 3);tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);return tmp;}else if (!isPad(d3) && isPad(d4)){b3 = base64Alphabet[d3];if ((b3 & 0x3) != 0)// last 2 bits should be zero{return null;}byte[] tmp = new byte[i * 3 + 2];System.arraycopy(decodedData, 0, tmp, 0, i * 3);tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));return tmp;}else{return null;}}else{ // No PAD e.g 3cQlb3 = base64Alphabet[d3];b4 = base64Alphabet[d4];decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);}return decodedData;}/*** remove WhiteSpace from MIME containing encoded Base64 data.** @param data the byte array of base64 data (with WS)* @return the new length*/private static int removeWhiteSpace(char[] data){if (data == null){return 0;}// count characters that's not whitespaceint newSize = 0;int len = data.length;for (int i = 0; i < len; i++){if (!isWhiteSpace(data[i])){data[newSize++] = data[i];}}return newSize;}
}
KaptchaTextCreator

package com.itsw;import com.google.code.kaptcha.text.impl.DefaultTextCreator;import java.util.Random;/*** 验证码文本生成器** @author ruoyi*/
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 = random.nextInt(3);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 (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]);}}suChinese.append("=?@" + result);return suChinese.toString();}
}
CaptchaConfig

properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.itswpu.....KaptchaTextCreator");

注意:双引号里改成你的KaptchaTextCreator所在的位置


package com.it...;import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import static com.google.code.kaptcha.Constants.*;/*** 验证码配置* * @author ruoyi*/
@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");// 验证码文本生成器properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.itswpu.....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;}
}
IdUtils

package com.itswpu.huanswpu.Captcha;import java.util.UUID;/*** ID生成器工具类* * @author ruoyi*/
public class IdUtils
{/*** 获取随机UUID* * @return 随机UUID*/public static String randomUUID(){return UUID.randomUUID().toString();}/*** 简化的UUID,去掉了横线* * @return 简化的UUID,去掉了横线*/public static String simpleUUID(){return UUID.randomUUID().toString();}}
RedisCache

package com.it..;import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;/*** spring redis 工具类** @author ruoyi**/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{@Autowiredpublic RedisTemplate redisTemplate;/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值*/public <T> void setCacheObject(final String key, final T value){redisTemplate.opsForValue().set(key, value);}/*** 缓存基本的对象,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 Redis键* @param timeout 超时时间* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout){return expire(key, timeout, TimeUnit.SECONDS);}/*** 设置有效时间** @param key Redis键* @param timeout 超时时间* @param unit 时间单位* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout, final TimeUnit unit){return redisTemplate.expire(key, timeout, unit);}/*** 获取有效时间** @param key Redis键* @return 有效时间*/public long getExpire(final String key){return redisTemplate.getExpire(key);}/*** 判断 key是否存在** @param key 键* @return true 存在 false不存在*/public Boolean hasKey(String key){return redisTemplate.hasKey(key);}/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T getCacheObject(final String key){ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 删除单个对象** @param key*/public boolean deleteObject(final String key){return redisTemplate.delete(key);}/*** 删除集合对象** @param collection 多个对象* @return*/public boolean deleteObject(final Collection collection){return redisTemplate.delete(collection) > 0;}/*** 缓存List数据** @param key 缓存的键值* @param dataList 待缓存的List数据* @return 缓存的对象*/public <T> long setCacheList(final String key, final List<T> dataList){Long count = redisTemplate.opsForList().rightPushAll(key, dataList);return count == null ? 0 : count;}/*** 获得缓存的list对象** @param key 缓存的键值* @return 缓存键值对应的数据*/public <T> List<T> getCacheList(final String key){return redisTemplate.opsForList().range(key, 0, -1);}/*** 缓存Set** @param key 缓存键值* @param dataSet 缓存的数据* @return 缓存数据的对象*/public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet){BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);Iterator<T> it = dataSet.iterator();while (it.hasNext()){setOperation.add(it.next());}return setOperation;}/*** 获得缓存的set** @param key* @return*/public <T> Set<T> getCacheSet(final String key){return redisTemplate.opsForSet().members(key);}/*** 缓存Map** @param key* @param dataMap*/public <T> void setCacheMap(final String key, final Map<String, T> dataMap){if (dataMap != null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/*** 获得缓存的Map** @param key* @return*/public <T> Map<String, T> getCacheMap(final String key){return redisTemplate.opsForHash().entries(key);}/*** 往Hash中存入数据** @param key Redis键* @param hKey Hash键* @param value 值*/public <T> void setCacheMapValue(final String key, final String hKey, final T value){redisTemplate.opsForHash().put(key, hKey, value);}/*** 获取Hash中的数据** @param key Redis键* @param hKey Hash键* @return Hash中的对象*/public <T> T getCacheMapValue(final String key, final String hKey){HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/*** 获取多个Hash中的数据** @param key Redis键* @param hKeys Hash键集合* @return Hash对象集合*/public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys){return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 删除Hash中的某条数据** @param key Redis键* @param hKey Hash键* @return 是否成功*/public boolean deleteCacheMapValue(final String key, final String hKey){return redisTemplate.opsForHash().delete(key, hKey) > 0;}/*** 获得缓存的基本对象列表** @param pattern 字符串前缀* @return 对象列表*/public Collection<String> keys(final String pattern){return redisTemplate.keys(pattern);}
}

3、试验效果

网址输入如图:

加了个前缀 生成验证码图片网址输入:

data:image/gif;base64,加 /9...

即可得到生成的图片

你的三连是我创作的最大动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/18288.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据仓库】BI看板DataEase入坑指南

开头夸夸国产开源BI软件DataEase&#xff0c;支持常见各种报表&#xff0c;还支持图表联动和上下级钻取&#xff0c;超赞有木有&#xff01;&#xff01;&#xff01; 再来为什么说入坑&#xff0c;源码启动各种不服啊。本地用的maven3.5一直导入不了Java项目backend。后来看了…

React-Native学习,RN的容器Flex-Box布局

justify-content&#xff08;在RN中属性名称为&#xff1a;justifyContent&#xff09;在主轴上对齐方式 align-items&#xff08;在RN中属性名称为&#xff1a;alignItems&#xff09;在交叉轴上的对齐方式 在React Native中&#xff0c;当没有设置容器的主轴方向时&#xf…

觉非科技李东旻:智能驾驶向大模型的进化与感知决策新范式的诞生

由亿欧汽车主办“AI与新一代车载智能感知融合创新论坛”近日登陆2023世界人工智能大会&#xff08;WAIC&#xff09;。觉非科技CEO李东旻受邀发表了“智能驾驶向大模型的进化与感知决策新范式的诞生”主题演讲&#xff0c;从产业趋势结合技术发展特点以及在感知决策中的能力应用…

【安卓12源码】WMS的作用及其启动流程

一、WMS 的作用 WMS 在 Android 系统的地位&#xff0c;它作为中间层&#xff0c;连接了上层的 View 框架和下层的 SurfaceFingler。 WMS 主要职责 窗口管理&#xff1a;负责启动、添加、删除窗口&#xff0c;管理窗口大小、层级&#xff0c;核心成员有&#xff1a;WindowCont…

Java的Hibernate框架中集合类数据结构的映射编写教程

Java的Hibernate框架中集合类数据结构的映射编写教程 一、集合映射 1.集合小介 集合映射也是基本的映射&#xff0c;但在开发过程中不会经常用到&#xff0c;所以不需要深刻了解&#xff0c;只需要理解基本的使用方法即可&#xff0c;等在开发过程中遇到了这种问题时能够查询…

elementui自定义loading图标

效果图如下&#xff1a; 一、在assets下新建一个mycss.css文件夹&#xff08;图片大小以及文字样式&#xff0c;可以根据自己的需求进行微调&#xff09; .el-loading-spinner {/*这个是自己想设置的 gif 加载动图*/background-image: url(../gif2.gif); background-repeat: n…

redis的分布式事务-redisson

一 redisson 1.1 redisson分布式事务 Redisson分布式锁是一种基于redis实现的分布式锁&#xff0c;它利用redis的setnx命令实现分布式锁的互斥访问。同时还支持锁的自动续期功能&#xff0c;可以避免因为某个进程崩溃或者网络故障导致锁无法释放的情况。 只要线程一加锁成功…

各种好看的css效果收集

CSS动画特效-多种方案实现CSS光束扫过&#xff0c;扫光特效&#xff0c;ae文字过光效果&#xff0c;光效移动效果 一个集合180种免费的线性渐变网站&#xff0c;可在任何网站使用您不仅可以复制渐变的原生CSS颜色代码&#xff0c;还可以查看下载每个优质的渐变图片。 链接&…

第二课:Figma 界面认识

创建文件 进入 Figma 后&#xff0c;可以查看最近浏览的内容&#xff0c;官方也推荐了一些基础的项目&#xff0c;点击右上角 Design file&#xff08;设计文件&#xff09;即可创建项目&#xff1b; 注&#xff1a;网页版和本地版界面样式布局一致。 创建画布 点击左上画框…

C++_简单模拟实现string的基本结构

C中&#xff0c;string早于STL问世。使用string中的构造函数可以实现对string类型的字符串的一系列操作。 今天来模拟C中的string的基本结构。注意仅仅是简单模拟&#xff0c;string内部结构其实非常复杂&#xff0c;并且不同版本的IDEstring的内部结构也不尽相同。尽管有所不…

FLAC格式如何转换为MP3?分享三种方法!

在数字音乐的世界中&#xff0c;FLAC和MP3是两种常见的音频格式。FLAC (Free Lossless Audio Codec)提供无损的音质&#xff0c;但文件大小较大。而MP3文件较小&#xff0c;更易于传输和保存&#xff0c;但可能牺牲一些音质。如果你想将FLAC音频转换成MP3格式&#xff0c;本文将…

java版本企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发

java版本企业电子招标采购系统源码Spring Cloud Spring Boot 二次开发 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标立项申请入口、用户可以保存为草…