Java中很輕松可以實現(xiàn)驗證碼功能,在原生AWT圖形化工具包中寫一點簡單的邏輯就能輕松完成驗證碼功能车摄。
本文寺谤,同時將google的kaptcha驗證碼一同講解。
項目地址:https://gitee.com/gester/captcha.git
同時吮播,推一下滑動驗證碼的原理與實現(xiàn)的文章变屁。文章地址:http://www.reibang.com/p/6ff29737209f
原創(chuàng)不易!如果有幫到您意狠,可以給作者一個小星星鼓勵下 ^ _ ^
功能
- 字符驗證碼
- AWT實現(xiàn)字符驗證碼
- kaptcha實現(xiàn)字符驗證碼
- 運算驗證碼
- AWT實現(xiàn)運算驗證碼
- kaptcha實現(xiàn)運算驗證碼
- 滑動驗證碼(擴展)
由于代碼量較大粟关,功能關聯(lián)性不強。一篇文章不夠清楚說明环戈,將在另外一篇文章講解闷板。
相關依賴
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
一. AWT實現(xiàn)驗證碼
字符驗證碼:
/**
* 生成字符驗證碼
* @return 字符驗證碼
*/
public static Map<String, Object> generatorCharVerificationCode() {
// 驗證碼圖片邊框?qū)挾? final int WIDTH = 150;
// 驗證碼圖片邊框高度
final int HEIGHT = 50;
// 驗證碼字符長度
int CHAR_LENGTH = 6;
// 驗證碼字體高度
int FONT_HEIGHT = HEIGHT - 12;
// 驗證碼干擾線條數(shù)
int INTERFERENCE_LINE = 4;
// 生成驗證碼所需字符
char[] charSequence = {
'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9'
};
Map<String, Object> verificationCodeMap = null;
// 生成透明rgb圖片
BufferedImage bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = bufferedImage.getGraphics();
// 備份原始畫筆顏色
Color color = graphics.getColor();
graphics.setColor(Color.BLACK);
// 圖片填充黑色
graphics.fillRect(0, 0, WIDTH, HEIGHT);
graphics.setColor(Color.WHITE);
// 圖片填充白色澎灸;組成黑色邊框的白色圖片
graphics.fillRect(1, 1, WIDTH - 2, HEIGHT - 2);
int newFontHeight = CHAR_LENGTH > 4 ? FONT_HEIGHT * 4 / CHAR_LENGTH : FONT_HEIGHT;
// 設置畫筆字體
Font font = new Font("微軟雅黑", Font.PLAIN, newFontHeight);
graphics.setFont(font);
// 根據(jù)系統(tǒng)時間創(chuàng)建隨機數(shù)對象
Random random = new Random(System.currentTimeMillis());
int r = 0;
int g = 0;
int b = 0;
// 驗證碼字符串
StringBuilder verificationCode = new StringBuilder();
for (int i = 0; i < CHAR_LENGTH; i++) {
char ch = charSequence[random.nextInt(charSequence.length)];
// 隨機生成rgb顏色值,并設置畫筆顏色
r = random.nextInt(255);
g = random.nextInt(255);
b = random.nextInt(255);
graphics.setColor(new Color(r, g, b));
// 根據(jù)畫筆顏色繪制字符
graphics.drawString(String.valueOf(ch), i * (newFontHeight), FONT_HEIGHT);
verificationCode.append(ch);
}
// 繪制干擾線
int x1, y1, x2, y2;
for (int i = 0; i < INTERFERENCE_LINE; i++) {
// 隨機生成rgb顏色值蛔垢,并設置畫筆顏色
r = random.nextInt(255);
g = random.nextInt(255);
b = random.nextInt(255);
graphics.setColor(new Color(r, g, b));
x1 = random.nextInt(WIDTH);
y1 = random.nextInt(HEIGHT);
x2 = random.nextInt(WIDTH);
y2 = random.nextInt(HEIGHT);
// 繪制線條
graphics.drawLine(x1, y1, x2, y2);
}
// 恢復畫筆顏色
graphics.setColor(color);
verificationCodeMap = new HashMap<String, Object>();
verificationCodeMap.put("verificationCodeImage", bufferedImage);
verificationCodeMap.put("verificationCode", verificationCode);
return verificationCodeMap;
}
public static void main(String[] args) throws IOException {
Map<String, Object> charMap = generatorCharVerificationCode();
BufferedImage bufferedImage1 = (BufferedImage) charMap.get("verificationCodeImage");
OutputStream outputStream1 = new FileOutputStream("C:/Users/Administrator/Desktop/charVerificationCodeImage.png");
ImageIO.write(bufferedImage1, "png", outputStream1);
System.out.println("驗證碼: " + charMap.get("verificationCode"));
outputStream1.flush();
outputStream1.close();
}
預覽圖
運算驗證碼:
static StringBuilder result = new StringBuilder(); // 運算驗證碼結(jié)果
/**
* 生成運算驗證碼
* @return 運算驗證碼
*/
public static Map<String, Object> generatorOperationVerificationCode() {
// 驗證碼圖片邊框?qū)挾? final int WIDTH = 185;
// 驗證碼圖片邊框高度
final int HEIGHT = 50;
// 驗證碼字體高度
int FONT_HEIGHT = HEIGHT - 12;
// 驗證碼干擾線條數(shù)
int INTERFERENCE_LINE = 4;
Map<String, Object> verificationCodeMap = null;
// 生成透明rgb圖片
BufferedImage bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = bufferedImage.getGraphics();
// 備份原始畫筆顏色
Color color = graphics.getColor();
graphics.setColor(Color.BLACK);
// 圖片填充黑色
graphics.fillRect(0, 0, WIDTH, HEIGHT);
graphics.setColor(Color.WHITE);
// 圖片填充白色击孩;組成黑色邊框的白色圖片
graphics.fillRect(1, 1, WIDTH - 2, HEIGHT - 2);
// 驗證碼字符串
String text = getText();
// 運算表達式
String operationExpression = text.substring(0, text.lastIndexOf("@") - 1);
// 計算結(jié)果
String result = text.substring(text.lastIndexOf("@") + 1, text.length());
int newFontHeight = operationExpression.length() > 4 ? FONT_HEIGHT * 4 / operationExpression.length() : FONT_HEIGHT;
// 設置畫筆字體
Font font = new Font("微軟雅黑", Font.PLAIN, FONT_HEIGHT);
graphics.setFont(font);
// 根據(jù)系統(tǒng)時間創(chuàng)建隨機數(shù)對象
Random random = new Random(System.currentTimeMillis());
int r = 0;
int g = 0;
int b = 0;
// 隨機生成rgb顏色值,并設置畫筆顏色
r = random.nextInt(255);
g = random.nextInt(255);
b = random.nextInt(255);
graphics.setColor(new Color(r, g, b));
// 根據(jù)畫筆顏色繪制字符
graphics.drawString(operationExpression, 5, FONT_HEIGHT);
// 繪制干擾線
int x1, y1, x2, y2;
for (int i = 0; i < INTERFERENCE_LINE; i++) {
// 隨機生成rgb顏色值鹏漆,并設置畫筆顏色
r = random.nextInt(255);
g = random.nextInt(255);
b = random.nextInt(255);
graphics.setColor(new Color(r, g, b));
x1 = random.nextInt(WIDTH);
y1 = random.nextInt(HEIGHT);
x2 = random.nextInt(WIDTH);
y2 = random.nextInt(HEIGHT);
// 繪制線條
graphics.drawLine(x1, y1, x2, y2);
}
// 恢復畫筆顏色
graphics.setColor(color);
verificationCodeMap = new HashMap<String, Object>();
verificationCodeMap.put("verificationCodeImage", bufferedImage);
verificationCodeMap.put("verificationCode", result);
return verificationCodeMap;
}
/**
* 獲取運算驗證碼
* @return 運算驗證碼
*/
public static String getText() {
Random random = new Random(System.currentTimeMillis());
int x = random.nextInt(51);
int y = random.nextInt(51);
int operationalRules = random.nextInt(4);
switch (operationalRules) {
case 0:
add(x, y);
break;
case 1:
subtract(x, y);
break;
case 2:
multiply(x, y);
break;
case 3:
divide(x, y);
break;
}
return result.toString();
}
/**
* 加法運算
* @param x 變量x
* @param y 變量y
*/
private static void add(int x, int y) {
result.append(x);
result.append(" + ");
result.append(y);
result.append(" = ?@");
result.append(x + y);
}
/**
* 減法運算
* @param x 變量x
* @param y 變量y
*/
private static void subtract(int x, int y) {
int max = Math.max(x, y);
int min = Math.min(x, y);
result.append(max);
result.append(" - ");
result.append(min);
result.append(" = ?@");
result.append(max - min);
}
/**
* 乘法運算
* @param x 變量x
* @param y 變量y
*/
private static void multiply(int x, int y) {
int value = x * y;
result.append(x);
result.append(value > 100 ? " + " : " * ");
result.append(y);
result.append(" = ?@");
result.append(value > 100 ? x + y : x * y);
}
/**
* 出發(fā)運算
* @param x 變量x
* @param y 變量y
*/
private static void divide(int x, int y) {
int max = Math.max(x, y);
int min = Math.min(x, y);
if (min == 0) {
multiply(max, min);
} else if (max % min == 0) {
result.append(max);
result.append(" / ");
result.append(min);
result.append(" = ?@");
result.append(max / min);
} else {
result.append(max);
result.append(" % ");
result.append(min);
result.append(" = ?@");
result.append(max % min);
}
}
public static void main(String[] args) throws IOException {
Map<String, Object> operationMap = generatorOperationVerificationCode();
BufferedImage bufferedImage2 = (BufferedImage) operationMap.get("verificationCodeImage");
OutputStream outputStream2 = new FileOutputStream("C:/Users/Administrator/Desktop/operationVerificationCodeImage.png");
ImageIO.write(bufferedImage2, "png", outputStream2);
System.out.println("驗證碼: " + operationMap.get("verificationCode"));
outputStream2.flush();
outputStream2.close();
}
預覽圖
總的來說巩梢,AWT實現(xiàn)驗證碼非常簡單,只需要編寫簡單的邏輯即可實現(xiàn)需要的驗證碼功能艺玲。代碼中的驗證碼可以根據(jù)字符長度和運算表達式長度適應畫布大小括蝠。但是,仍是不完美饭聚;這時忌警,kaptcha就登場了,我們?yōu)樯兑褂胟apcha秒梳,kapcha的優(yōu)勢有什么法绵?
kaptcha驗證碼是谷歌編寫開源的,也是基于AWT實現(xiàn)酪碘。但是功能更加豐富朋譬,可以配置背景、畫布兴垦、尺寸徙赢、顏色、樣式探越、噪點等等諸多好處狡赐。項目具有輕量級、功能豐富钦幔、易于上手枕屉,且是大公司開源,穩(wěn)定性有保障节槐。為何不用搀庶?
kaptcha實現(xiàn)字符驗證碼:
默認情況下,kaptcha是默認生成英文數(shù)字組合的字符驗證碼铜异。但是哥倔,這里功能很強大,可以自己編寫驗證碼的生成器規(guī)則揍庄,注冊到kapcha攔截器中咆蒿,即可使用。運算驗證碼就是使用這一種方式,待會再議沃测。
配置類代碼:
@Configuration
public class CaptchaConfig
{
@Bean(name = "captchaProducer")
public DefaultKaptcha getKaptchaBean()
{
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有邊框 默認為true 我們可以自己設置yes缭黔,no
properties.setProperty("kaptcha.border", "yes");
// 邊框顏色 默認為Color.BLACK
properties.setProperty("kaptcha.border.color", "black");
// 驗證碼文本字符顏色 默認為Color.BLACK
properties.setProperty("kaptcha.textproducer.font.color", "black");
// 驗證碼圖片寬度 默認為200
properties.setProperty("kaptcha.image.width", "150");
// 驗證碼圖片高度 默認為50
properties.setProperty("kaptcha.image.height", "60");
// 驗證碼文本字符大小 默認為40
properties.setProperty("kaptcha.textproducer.font.size", "30");
// KAPTCHA_SESSION_KEY
properties.setProperty("kaptcha.session.key", "kaptchaCharCode");
// 驗證碼文本字符間距 默認為2
properties.setProperty("kaptcha.textproducer.char.space", "3");
// 驗證碼文本字符長度 默認為4
properties.setProperty("kaptcha.textproducer.char.length", "4");
// 驗證碼文本字體樣式 默認為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty("kaptcha.textproducer.font.names", "微軟雅黑");
// 驗證碼噪點顏色 默認為Color.BLACK
properties.setProperty("kaptcha.noise.color", "black");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
生成字符驗證碼
/**
* 獲取字符驗證碼
*
* @param imageVerificationDto 用戶信息
* @return 字符驗證碼
* @throws ServiceException 獲取字符驗證碼異常
*/
private ImageVerificationVo selectCharVerificationCode(ImageVerificationDto imageVerificationDto) throws ServiceException {
byte[] bytes = null;
String text = "";
BufferedImage bufferedImage = null;
ImageVerificationVo imageVerificationVo = null;
try {
imageVerificationVo = new ImageVerificationVo();
// 生成字符驗證碼文本
text = captchaProducer.createText();
// 生成字符驗證碼圖片
bufferedImage = captchaProducer.createImage(text);
getRequest().getSession().setAttribute("imageVerificationVo", imageVerificationVo);
// 在分布式應用中,可將session改為redis存儲
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
bytes = byteArrayOutputStream.toByteArray();
// 圖片base64加密
imageVerificationVo.setCharImage(Base64Utils.encodeToString(bytes));
imageVerificationVo.setType(imageVerificationDto.getType() == null ? "char" : imageVerificationDto.getType());
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new ServiceException(ServiceExceptionCode.SELECT_VERIFICATION_CODE_ERROR);
}
return imageVerificationVo;
}
預覽圖
kaptcha實現(xiàn)運算驗證碼
配置類
@Configuration
public class CaptchaConfig
{
@Bean(name = "captchaProducerMath")
public DefaultKaptcha getKaptchaBeanMath()
{
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有邊框 默認為true 我們可以自己設置yes蒂破,no
properties.setProperty("kaptcha.border", "no");
// 邊框顏色 默認為Color.BLACK
properties.setProperty("kaptcha.border.color", "55,160,204");
// 驗證碼文本字符顏色 默認為Color.BLACK
properties.setProperty("kaptcha.textproducer.font.color", "blue");
// 背景漸變色馏谨,開始顏色
properties.setProperty("kaptcha.background.clear.from", "234,172,236");
// 背景漸變色,結(jié)束顏色
properties.setProperty("kaptcha.background.clear.to", "234,144,115");
// 驗證碼圖片寬度 默認為200
properties.setProperty("kaptcha.image.width", "170");
// 驗證碼圖片高度 默認為50
properties.setProperty("kaptcha.image.height", "60");
// 驗證碼文本字符大小 默認為40
properties.setProperty("kaptcha.textproducer.font.size", "35");
// KAPTCHA_SESSION_KEY
properties.setProperty("kaptcha.session.key", "kaptchaMathCode");
// --------------驗證碼文本生成器,這里需要設置成自己項目的包名----------------------
properties.setProperty("kaptcha.textproducer.impl", "com.selfimpr.captcha.config.KaptchaMathTextCreator");
// 驗證碼文本字符間距 默認為2
properties.setProperty("kaptcha.textproducer.char.space", "3");
// 驗證碼文本字符長度 默認為9
properties.setProperty("kaptcha.textproducer.char.length", "9");
// 驗證碼文本字體樣式 默認為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
// 驗證碼噪點顏色 默認為Color.BLACK
properties.setProperty("kaptcha.noise.color", "243,79,67");
// 干擾實現(xiàn)類
// 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.ShadowGimpy
// properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
注意附迷。這里注冊了自定義驗證碼生成器
自定義運算器惧互,同AWT生成運算驗證碼的運算方法沒有區(qū)別。
package com.selfimpr.captcha.config;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
import java.util.Random;
/**
* @Description: 運算驗證碼
* -------------------
* @Author: YangXingfu
* @Date: 2019/07/12 09:11
*/
public class KaptchaMathTextCreator extends DefaultTextCreator {
StringBuilder result = new StringBuilder();
@Override
public String getText() {
Random random = new Random(System.currentTimeMillis());
int x = random.nextInt(51);
int y = random.nextInt(51);
int operationalRules = random.nextInt(4);
switch (operationalRules) {
case 0 : add(x, y); break;
case 1 : subtract(x, y); break;
case 2 : multiply(x, y); break;
case 3 : divide(x, y); break;
}
return result.toString();
}
private void add(int x, int y) {
result.append(x);
result.append(" + ");
result.append(y);
result.append(" = ?@");
result.append(x + y);
}
private void subtract(int x, int y) {
int max = Math.max(x, y);
int min = Math.min(x, y);
result.append(max);
result.append(" - ");
result.append(min);
result.append(" = ?@");
result.append(max - min);
}
private void multiply(int x, int y) {
int value = x * y;
result.append(x);
result.append(value > 100 ? " + " : " * ");
result.append(y);
result.append(" = ?@");
result.append(value > 100 ? x + y : x * y);
}
private void divide(int x, int y) {
int max = Math.max(x, y);
int min = Math.min(x, y);
if (min == 0) {
multiply(max, min);
} else if(max % min == 0) {
result.append(max);
result.append(" / ");
result.append(min);
result.append(" = ?@");
result.append(max / min);
} else {
result.append(max);
result.append(" % ");
result.append(min);
result.append(" = ?@");
result.append(max % min);
}
}
}
生成運算驗證碼
**
* 獲取運算驗證碼
*
* @param imageVerificationDto 用戶信息
* @return 運算驗證嗎
* @throws ServiceException 查詢運算驗證碼異常
*/
private ImageVerificationVo selectOperationVerificationCode(ImageVerificationDto imageVerificationDto) throws ServiceException {
byte[] bytes = null;
String text = "";
BufferedImage bufferedImage = null;
ImageVerificationVo imageVerificationVo = null;
try {
imageVerificationVo = new ImageVerificationVo();
imageVerificationVo.setType(imageVerificationDto.getType());
// 生成運算驗證碼文本
text = captchaProducerMath.createText();
String value = text.substring(0, text.lastIndexOf("@"));
// 生成運算驗證碼圖片
bufferedImage = captchaProducerMath.createImage(value);
// 驗證碼存入redis
getRequest().getSession().setAttribute("imageVerificationVo", imageVerificationVo);
// 在分布式應用中喇伯,可將session改為redis存儲
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
bytes = byteArrayOutputStream.toByteArray();
// 圖片base64加密
imageVerificationVo.setOperationImage(Base64Utils.encodeToString(bytes));
imageVerificationVo.setType(imageVerificationDto.getType());
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new ServiceException(ServiceExceptionCode.SELECT_VERIFICATION_CODE_ERROR);
}
return imageVerificationVo;
}
后話
這里的驗證碼樣式喊儡、字體等都有點丑,原諒作者的藝術水平太高稻据,大大們可自行調(diào)整艾猜。kapcha驗證碼與手動編寫的驗證碼可以明顯看出kaptcha驗證碼的優(yōu)勢,推薦大家使用kaptcha來做驗證碼捻悯。
有了這些驗證碼為什么會有新的驗證碼匆赃?值得大家思考的一個問題。
項目地址:https://gitee.com/gester/captcha.git
如果這篇文章有幫到您今缚,可以給個star炸庞,謝謝大大。
同時荚斯,推薦一波當下流行的滑動驗證碼,驗證碼實現(xiàn)和京東的滑動驗證碼沒有多大區(qū)別查牌。下面給個傳送門事期,給個預覽圖哈。
滑動驗證碼原理與實現(xiàn)文章地址:http://www.reibang.com/p/6ff29737209f