之前做的一個點選驗證碼,把代碼貼出來,如果有需要可以根據實際情況修改
普通驗證碼:https://blog.csdn.net/weisong530624687/article/details/78861721
JAVA Graphics實作變色、漸變、陰影、傾斜、立體:
https://blog.csdn.net/weisong530624687/article/details/80048478
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.Random;
public abstract class AbstractRandCode {
/**
* 驗證碼緩存
*/
public static final String LOGIN_CAPTCHA = "_LOGIN_CAPTCHA";
public static final String LOGIN_CAPTCHA_NAMESPACE = "RandCode";
/**
* 随機碼Img
*/
private BufferedImage bufferedImage;
/**
* 國際化語言
*/
private String language;
public BufferedImage getBufferedImage() {
return bufferedImage;
}
public void setBufferedImage(BufferedImage bufferedImage) {
this.bufferedImage = bufferedImage;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
/**
* 擷取給定範圍随機色
* @param basicValue
* @param extentValue
* @return
*/
protected Color getRandColor(int basicValue, int extentValue) {
Random random = new Random();
int basic = basicValue > 255 ? 255 : basicValue;
int extent = extentValue > 255 ? 255 : extentValue;
int r = basic + random.nextInt(extent - basic);
int g = basic + random.nextInt(extent - basic);
int b = basic + random.nextInt(extent - basic);
return new Color(r, g, b);
}
}
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.imageio.ImageIO;
import com.alibaba.fastjson.JSONArray;
/**
* 點選驗證碼
*/
public class PointSelectRandCode extends AbstractRandCode implements RandCode {
Log log = LogFactory.getLog(PointSelectRandCode.class);
/**
* 生成驗證碼的寬度
*/
private static final int WIDTH = 230;
/**
* 生成驗證碼的高度
*/
private static final int HEIGHT = 100;
/**
* 底部trip資訊高度
*/
private static final int BOTTOM_HEIGHT = 35;
/**
* 字元大小
*/
private static final int FONT_SIZE = 24;
/**
* 定義點選文字圖檔驗證碼允許的誤內插補點,機關是px
*/
private static final double ERROR_AMOUNT = FONT_SIZE / 3 * 2.0;
/**
* 幹擾線個數
*/
private static final int LINE_COUNT = 120;
/**
* 設定字元位置數
*/
private static final Integer[] PLACE_ARRAY = new Integer[] {1, 2, 3, 4, 5};
/**
* 需要校驗的字元個數
*/
private static final int VALIDATE_CHAR_NUM = 3;
/**
* 字元的顔色随機範圍
*/
@SuppressWarnings("unused")
private static final Color[] CHAR_COLORS =
{Color.BLACK, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.PINK, Color.ORANGE};
/**
* 字元的顔色随機範圍
*/
//private static final Color[] LIGHT_CHAR_COLORS = {Color.CYAN, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.WHITE, Color.YELLOW};
private static final Color[] LIGHT_CHAR_COLORS = {new Color(72, 76, 119)};
private static String separator = File.separator;
/**
* 圖檔資源路徑
*/
private static final String IMG_PATH = separator + "static-res" + separator + "img" + separator + "login" + separator;
/**
* 背景圖檔資源路徑
*/
private static final String BACK_GROUD_IMG_PATH = IMG_PATH + "backgroundimg" + separator;
/**
* 是否設定随機線
*/
private boolean isSetRandLine = false;
/**
* 校驗隊列
*/
List<Point> validatedList;
/**
* 頁面展示使用者操作提示
*/
String showOperatMessage;
/**
* 資源路徑位址
*/
String realPath;
/**
* 随機碼隊列
*/
private List<String> randCodeList = new ArrayList<>();
@Override
public boolean checkRandCode(String checkCode, String checkCodeKey) {
CacheServiceByRedis redisCache = new CacheServiceByRedis();
String redisValidatedJson = redisCache.getVal(LOGIN_CAPTCHA_NAMESPACE, checkCodeKey);
List<Point> redisValidatedList = JSONArray.parseArray(redisValidatedJson, Point.class);
redisCache.remove(LOGIN_CAPTCHA_NAMESPACE, checkCodeKey);
if (!checkRedisValidatedList(redisValidatedList)) {
return false;
}
//校驗checkCode
String[] checkCodeArray = checkCode.split(",");
if (!checkCheckCode(checkCodeArray)) {
return false;
}
//比對checkCode和redisValidate 判斷坐标參數是否正确
for (int i = 0; i < checkCodeArray.length; i++) {
String[] checkCodePoint = checkCodeArray[i].split("_");
Point centerPoint = redisValidatedList.get(i);
Point randPoint = new Point();
randPoint.setLocation(new Double(checkCodePoint[0]), new Double(checkCodePoint[1]));
if (!isInnerCircle(randPoint, centerPoint, ERROR_AMOUNT)) {
return false;
}
}
return true;
}
/**
* 擷取圓心
* @param point
* @return
*/
@SuppressWarnings("unused")
private Point getCenterPoint(Point point) {
Point centerPoint = new Point();
centerPoint.setLocation(point.getX() + FONT_SIZE / 2.0, point.getY() + FONT_SIZE / 2.0);
return centerPoint;
}
/**
* 判斷是否在圓形内
* 判斷點與圓心之間的距離和圓半徑的關系
* @param randPoint
* @param centerPoint
* @return
*/
private boolean isInnerCircle(Point randPoint, Point centerPoint, double radius) {
double distance = Math.hypot(randPoint.getX() - centerPoint.getX(), randPoint.getY() - centerPoint.getY());
return distance > radius ? false : true;
}
private boolean checkCheckCode(String[] checkCodeArray) {
if (checkCodeArray.length != VALIDATE_CHAR_NUM) {
return false;
}
for (int i = 0; i < checkCodeArray.length; i++) {
if (EmptyUtils.isEmpty(checkCodeArray[i])) {
return false;
}
}
return true;
}
private boolean checkRedisValidatedList(List<Point> redisValidatedList) {
return EmptyUtils.isNotEmpty(redisValidatedList) && redisValidatedList.size() == VALIDATE_CHAR_NUM;
}
@Override
public AbstractRandCode getRandCode() {
randCodeList = new ArrayList<>();
return getPointSelectRandCode(isEnLanguage(getLanguage()));
}
private AbstractRandCode getPointSelectRandCode(boolean isEnLanguage) {
//生成背景圖檔
BufferedImage pointSelectImg = getBackGround();
Graphics graphics = pointSelectImg.getGraphics();
setGraphicsFont(isEnLanguage, graphics);
//随機排序位置集合
@SuppressWarnings({"unchecked", "rawtypes"})
CopyOnWriteArrayList<Integer> randPlaceList = new CopyOnWriteArrayList(Arrays.asList(PLACE_ARRAY));
Collections.shuffle(randPlaceList);
//list參數坐标參數 用于存儲校驗原點坐标
List<Point> validatedCodeList = new ArrayList<>();
//記錄要校驗的字元
StringBuilder readyClickCharBuilder = new StringBuilder();
//随機N個place不需要校驗
List<Integer> notValidatedPlaces = getNotValidatePlaces();
Point pointLast = null;
for (int i = 0; i < PLACE_ARRAY.length; i++) { // 5個漢字,隻點4個
String randChar = getRandChar(true);
randCodeList.add(randChar);
int place = randPlaceList.get(i);
Point point = getPoint(place);
while (isCloser(point, pointLast)) {
point = getPoint(place);
}
pointLast = point;
Point pointNew = setRotateAndGradient(randChar, point.x, point.y, graphics);
if (!notValidatedPlaces.contains(place)) {
readyClickCharBuilder.append(randChar);
validatedCodeList.add(pointNew);
}
}
String operateMessage = getOperateMessage(isEnLanguage, readyClickCharBuilder);
BufferedImage bufferBottomImg = getBottomImg(operateMessage, isEnLanguage);
BufferedImage customImg = getCustomImg();
BufferedImage combinedImg = getCombinedImg(pointSelectImg, bufferBottomImg, customImg);
PointSelectRandCode pointSelectRandCode = new PointSelectRandCode();
pointSelectRandCode.setBufferedImage(combinedImg);
pointSelectRandCode.setShowOperatMessage(operateMessage);
pointSelectRandCode.setValidatedList(validatedCodeList);
return pointSelectRandCode;
}
private List<Integer> getNotValidatePlaces() {
List<Integer> notValidatedPlaces = new ArrayList<>();
Random random = new Random();
while (true) {
Integer notValidatedPlace = random.nextInt(PLACE_ARRAY.length) + 1;
if (!notValidatedPlaces.contains(notValidatedPlace)) {
notValidatedPlaces.add(notValidatedPlace);
if (notValidatedPlaces.size() == (PLACE_ARRAY.length - VALIDATE_CHAR_NUM)) {
break;
}
}
}
return notValidatedPlaces;
}
private void setGraphicsFont(boolean isEnLanguage, Graphics graphics) {
if (isEnLanguage) {
graphics.setFont(new Font("Times New Roman", Font.BOLD, FONT_SIZE));
} else {
graphics.setFont(new Font("宋體", Font.BOLD, FONT_SIZE));
}
}
private boolean isCloser(Point point, Point pointLast) {
if (EmptyUtils.isAnyoneEmpty(pointLast)) {
return false;
}
if (Math.abs(point.getX() - pointLast.getX()) > FONT_SIZE || Math.abs(point.getY() - pointLast.getY()) > FONT_SIZE) {
return false;
}
return true;
}
/**
* 擷取合并後的最終Img
* @param pointSelectImg
* @param bufferBottomImg
* @param customImg
* @return
*/
private BufferedImage getCombinedImg(BufferedImage pointSelectImg, BufferedImage bufferBottomImg, BufferedImage customImg) {
BufferedImage combinedImg =
new BufferedImage(WIDTH, HEIGHT + bufferBottomImg.getHeight() + customImg.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics combinedGrap = combinedImg.getGraphics(); //合并
combinedGrap.drawImage(pointSelectImg, 0, 0, null);
combinedGrap.drawImage(customImg, 0, pointSelectImg.getHeight(), null);
combinedGrap.drawImage(bufferBottomImg, 0, pointSelectImg.getHeight() + customImg.getHeight(), null);
return combinedImg;
}
/**
* 擷取自定義img
* @return
*/
private BufferedImage getCustomImg() {
BufferedImage customImg = new BufferedImage(WIDTH, 15, BufferedImage.TYPE_INT_RGB);
Graphics gra = customImg.getGraphics();
gra.setColor(new Color(60, 59, 65));
gra.fillRect(0, 0, customImg.getWidth(), customImg.getHeight());
return customImg;
}
/**
* 旋轉并且畫出指定字元串
* @param drawString 需要旋轉的字元串
* @param x 字元串的x坐标
* @param y 字元串的Y坐标
* @param graphics 畫筆g
* @return
*/
private Point setRotateAndGradient(String drawString, int x, int y, Graphics graphics) {
Point point = new Point();
//如果在畫字之前旋轉圖檔 degree 旋轉的角度
Random random = new Random();
int maxDegree = random.nextInt(2) % 2 == 0 ? 0 : 305;
int degree = random.nextInt(45) + maxDegree;
Graphics2D graphics2d = (Graphics2D) graphics.create();
//平移原點到圖形環境的中心 ,這個方法的作用實際上就是将字元串移動到某一個位置
graphics2d.translate(x, y);
//旋轉文本
double degreeValue = degree * Math.PI / 180;
graphics2d.rotate(degreeValue);
double newPointX = Math.cos(degreeValue) * (FONT_SIZE / 2) + x;
double newPointY = Math.sin(degreeValue) * (FONT_SIZE / 2) + y;
point.setLocation(newPointX, newPointY);
//建立循環漸變的GraphientPaint對象
GradientPaint paint =
new GradientPaint(x, y, LIGHT_CHAR_COLORS[random.nextInt(LIGHT_CHAR_COLORS.length)], x + 24, y + 24, LIGHT_CHAR_COLORS[random.nextInt(LIGHT_CHAR_COLORS.length)], true);
graphics2d.setPaint(paint);
//特别需要注意的是,這裡的畫筆已經具有了上次指定的一個位置,是以這裡指定的其實是一個相對位置
graphics2d.drawString(drawString, 0, 0);
return point;
}
private String getOperateMessage(boolean isEnLanguage, StringBuilder readyClickCharBuilder) {
StringBuilder operatMessage = new StringBuilder();
if (isEnLanguage) {
operatMessage.append(" Click In Order");
} else {
operatMessage.append(" 請依次點選");
}
for (int i = 0; i < readyClickCharBuilder.length(); i++) {
char charAt = readyClickCharBuilder.charAt(i);
operatMessage.append(" \"" + charAt + "\"");
}
return operatMessage.toString();
}
/**
* 自定義每個位置的随機坐标
* @param place
* @return
*/
private Point getPoint(int place) {
int x = 0;
int maxDynamicRange = WIDTH / 5 / 3;
int minDynamicRange = WIDTH / 5 / 3;
switch (place) {
case 1:
x = new Random().nextInt(maxDynamicRange) + minDynamicRange;
break;
case 2:
x = new Random().nextInt(maxDynamicRange) + minDynamicRange + WIDTH / 5;
break;
case 3:
x = new Random().nextInt(maxDynamicRange) + minDynamicRange + WIDTH / 5 * 2;
break;
case 4:
x = new Random().nextInt(maxDynamicRange) + minDynamicRange + WIDTH / 5 * 3;
break;
case 5:
x = new Random().nextInt(maxDynamicRange) + WIDTH / 5 * 4;
break;
default:
break;
}
int y = new Random().nextInt(HEIGHT / 3) + HEIGHT / 3;
if (place == 5) {
y = new Random().nextInt(HEIGHT / 6 * 4) + HEIGHT / 6 * 2;
}
return new Point(x, y);
}
/**
* 擷取頂部要展示的Image
* @param operateMessage
* @return
*/
private BufferedImage getBottomImg(String operateMessage, boolean isEnLanguage) {
// 建立底部圖檔
BufferedImage bufferImgTop = new BufferedImage(WIDTH, BOTTOM_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics gra = bufferImgTop.getGraphics();
gra.fillArc(0, 0, WIDTH, BOTTOM_HEIGHT, 20, 50);
String filePath = getRealPath() + IMG_PATH + "white.png";
File file = new File(filePath);
BufferedImage sourceImg = null;
try {
sourceImg = ImageIO.read(new FileInputStream(file));
} catch (Exception e) {
log.error(e);
throw new BusinessException("擷取驗證碼失敗");
}
//2.内部圓角處理
//填充指定的矩形
gra.setColor(new Color(60, 59, 65));
gra.fillRect(0, 0, WIDTH, BOTTOM_HEIGHT);
gra.drawImage(sourceImg, 0, 0, WIDTH, BOTTOM_HEIGHT, null);
//1.内部不做圓角處理
//gra.setColor(new Color(247, 249, 250));
//gra.fillRect(0, 0, bufferImgTop.getWidth(), bufferImgTop.getHeight());
// 設定文字背景顔色
Font font = new Font("宋體", 0, 14);
gra.setFont(font);
gra.setColor(Color.DARK_GRAY);
if (isEnLanguage) {
gra.drawString(operateMessage, 20, bufferImgTop.getHeight() / 2 + font.getSize() / 2);
} else {
gra.drawString(operateMessage, 32, bufferImgTop.getHeight() / 2 + font.getSize() / 2);
}
return bufferImgTop;
}
/**
* 生成背景圖檔
* @return
*/
private BufferedImage getBackGround() {
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
//建立Graphics2D對象
Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.fillArc(0, 0, WIDTH, HEIGHT, 20, 50);
Random random = new Random();
String filePath = getRealPath() + BACK_GROUD_IMG_PATH;
File file = new File(filePath);
File[] picFiles = file.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
if (file.isDirectory()) {
return false;
}
BufferedImage bi;
try {
bi = ImageIO.read(file);
} catch (IOException e) {
log.info(e);
return false;
}
if (bi == null) {
return false;
}
return true;
}
});
File picture = picFiles[random.nextInt(picFiles.length)];
BufferedImage sourceImg = null;
try {
sourceImg = ImageIO.read(new FileInputStream(picture));
} catch (Exception e) {
log.error(e);
throw new BusinessException("擷取驗證碼失敗");
}
//2.内部圓角處理
//填充指定的矩形
graphics.setColor(new Color(60, 59, 65));
graphics.fillRect(0, 0, WIDTH, HEIGHT);
graphics.drawImage(sourceImg, 0, 0, WIDTH, HEIGHT, null);
BasicStroke basicStroke = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
graphics.setStroke(basicStroke);
setRandLine(graphics);
//輸出生成的驗證碼圖檔
graphics.dispose();
return image;
}
/**
* 設定随機線
* @param graphics
*/
private void setRandLine(Graphics2D graphics) {
if (!isSetRandLine) {
return;
}
//繪制随機線數量/粗細/顔色/位置
for (int i = 0; i < LINE_COUNT; i++) {
Line2D line = getRandLine();
graphics.draw(line);
graphics.setColor(getRandColor(30, 150));
}
}
/**
* 擷取随機線
* @return
*/
private Line2D getRandLine() {
Random random = new Random();
int x = random.nextInt(WIDTH - 1);
int y = random.nextInt(HEIGHT - 1);
int x1 = random.nextInt(100) + 1;
int y1 = random.nextInt(120) + 1;
return new Line2D.Double(x, y, x + x1, y + y1);
}
private String getRandChar(boolean isEnLanguage) {
if (isEnLanguage) {
return getRandomEnChar();
}
return getRandomChineseChar();
}
private String getRandomEnChar() {
//混合字元串
String mixString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
String randomEnChar = String.valueOf(mixString.charAt(random.nextInt(mixString.length())));
while (randCodeList.contains(randomEnChar)) {
randomEnChar = getRandomEnChar();
}
return randomEnChar;
}
/**
* 擷取随機中文字元
* @return
*/
private String getRandomChineseChar() {
Random random = new Random();
Integer highPosition = 176 + Math.abs(random.nextInt(39));
Integer lowPosition = 161 + Math.abs(random.nextInt(93));
byte[] b = new byte[2];
b[0] = highPosition.byteValue();
b[1] = lowPosition.byteValue();
try {
return new String(b, "GBk");
} catch (UnsupportedEncodingException e) {
log.info(e);
return getRandomChineseChar();
}
}
private boolean isEnLanguage(String language) {
return !(EmptyUtils.isEmpty(language) || "zh_CN".equals(language));
}
public List<Point> getValidatedList() {
return validatedList;
}
public void setValidatedList(List<Point> validatedList) {
this.validatedList = validatedList;
}
public String getShowOperatMessage() {
return showOperatMessage;
}
public void setShowOperatMessage(String showOperatMessage) {
this.showOperatMessage = showOperatMessage;
}
public String getRealPath() {
return realPath;
}
public void setRealPath(String realPath) {
this.realPath = realPath;
}
}
打賞
如果覺得我的文章對你有幫助,有錢就捧個錢場,沒錢就捧個人場,歡迎點贊或轉發 ,并請注明原出處,謝謝....