本次文章比较无聊,使用场景比较少,我也不知道为什么公司要整这个玩意,不花钱直接买,但是我只是个工具人,上头怎么说我怎么做

需求背景
-
公司原有的图片验证码是php,现在要抛弃改成java,在此基础上增加滑动验证码。 -
目前网络讲的清楚的关于滑动验证码实现的很少,所以想着实现了之后share一下。
先放出个效果图先
需求分析
整个滑动验证码其实划分成三个部分
-
滑动验证码的底部大图(底图)
-
类似拼图一样可以拖动的图(滑块图)
-
和滑块图形状一样的需要被填充的阴影部分(阴影图)
看我干嘛?需求都有了,开整呀!
具体实现
1.导入滑动验证码所需的底部大图
就是一些比较基本的IO和图片操作,自行食用把
/**
* 获取图片BufferImage
* <p>
* 1.读取一张照片
*/
public BufferedImage getPicture() throws IOException {
String imageName = "image" + RandomUtils.nextInt(5) + ".png";
String imageString = (String) redisOperations.get(SLIDE_IMAGE_CODE_KEY_PREFIX + imageName);
if(StringUtils.isEmpty(imageString)){
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/img/" + imageName);
BufferedImage read = ImageIO.read(resourceAsStream);
String imageStringToRedis = encodeToString(read);
redisOperations.set(SLIDE_IMAGE_CODE_KEY_PREFIX+imageName,imageStringToRedis);
return read;
}
return decodeToImage(imageString);
}
2.滑块图的实现
仔细观察滑块图,无非就是由一个正方形和两个半圆组合在一块。
球球了!这个两个半圆最好别在同一边。因为是真的丑!!
具体实现步骤及代码如下:
(1)生成滑块图的拼图形状
/**
* 中心矩形面积添加或减去半圆
*
* @param area
* @param edge
* @param circularType
* @return
*/
public Shape createCircular(Area area, int edge, int circularType) {
Shape circle = null;
/**
* cursor(游标):圆的右上角坐标随机化 cursor ∈[Padding+Margin,Padding+Width-Diameter-Margin]
*/
int cursor = RandomUtils.nextInt(74 - 32 + 1) + 32;
int margin = JIGSAW_MARGIN - (CIRCLE_DIAMETER / 2);
if (edge == LEFT_EDGE) {
circle = new Arc2D.Double(margin, cursor, CIRCLE_DIAMETER, CIRCLE_DIAMETER, 0, 360, Arc2D.OPEN);
} else if (edge == TOP_EDGE) {
circle = new Arc2D.Double(cursor, margin, CIRCLE_DIAMETER, CIRCLE_DIAMETER, 0, 360, Arc2D.OPEN);
} else if (edge == RIGHT_EDGE) {
circle = new Arc2D.Double(JIGSAW_WIDTH + margin, cursor, CIRCLE_DIAMETER, CIRCLE_DIAMETER, 0, 360, Arc2D.OPEN);
} else {
circle = new Arc2D.Double(cursor, JIGSAW_WIDTH + margin, CIRCLE_DIAMETER, CIRCLE_DIAMETER, 0, 360, Arc2D.OPEN);
}
/**
* 判断圆的类型,区域做加减
*/
if (circularType == CONVEX_CIRCLE) {
area.add(new Area(circle));
} else {
area.subtract(new Area(circle));
}
return area;
}
/**
* 获取拼图形状
* 正方形的四边:{ 左边:0,上边:1,右边:2,下边:3 }
* 凸圆:0 凹圆:1
*
* @return
*/
public Shape getPicShape() {
int edge = RandomUtils.nextInt(4);
int circularTypeOne = RandomUtils.nextInt(2);
int edgeTwo = RandomUtils.nextInt(4);
while (edgeTwo == edge) {
edgeTwo = RandomUtils.nextInt(4);
}
int circularTypeTwo = RandomUtils.nextInt(2);
Area area = new Area();
Shape square = new Rectangle2D.Double(JIGSAW_MARGIN, JIGSAW_MARGIN, JIGSAW_WIDTH, JIGSAW_HEIGHT);
area.add(new Area(square));
createCircular(area, edge, circularTypeOne);
createCircular(area, edgeTwo, circularTypeTwo);
return area;
}
主要的逻辑通过一个正方形来两个圆形做并集和差集,示意图如下
(2)生成滑块图
获取了拼图的形状之后,就要那这个形状随机的在底层上扣出来一块来,但是这个区域一般选择底图的右半部分,而且要注意上边界和下边界的选取一定要在图片内
/**
* 处理模板图生成不规则图片
*/
public BufferedImage handlerPic(BufferedImage template, Shape imageShape) {
BufferedImage cut = template;
Shape shape = imageShape;
Graphics2D graphics = cut.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.draw(shape);
BufferedImage bfm = new BufferedImage(TAILORING_WIDTH, TAILORING_HEIGHT, BufferedImage.TYPE_INT_ARGB);
shadowLayer.set(new BufferedImage(TAILORING_WIDTH, TAILORING_HEIGHT, BufferedImage.TYPE_INT_ARGB));
for (int i = 0; i < TAILORING_WIDTH; i++) {
for (int j = 0; j < TAILORING_HEIGHT; j++) {
if (shape.contains(i, j)) {
bfm.setRGB(i, j, template.getRGB(i, j));
}
if (shape.contains(i, j)) {
shadowLayer.get().setRGB(i, j, Color.black.getRGB());
}
}
}
BufferedImage resultImgBuff = dealLightAndShadow(bfm, shape);
return resultImgBuff;
}
3.阴影图的实现
在上面的过程中,我们已经生成了滑块图的形如拼图的形状,那我们就可以用这个形状去底图生成阴影图,就是将底图处理成带有阴影图的样子。
/**
* 在大图增加阴影部分,即填充部分
*
* @param image
* @param sourceImage
* @return
*/
public BufferedImage addWaterMark(BufferedImage image, BufferedImage sourceImage, Shape shape) {
BufferedImage source = sourceImage;
Graphics2D graphics = source.createGraphics();
graphics.setColor(Color.white);
graphics.setStroke(new BasicStroke(SHADOW_WIDTH));
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.6F));
BufferedImage waterMark = image;
handler(waterMark.createGraphics(), SHADOW_WIDTH, shape);
shadowHandle(waterMark.createGraphics(), SHADOW_WIDTH, shape);
shadowHandleBlack(waterMark.createGraphics(), SHADOW_WIDTH, shape);
graphics.drawImage(waterMark, locationX.get(), locationY.get(), null);
graphics.dispose();
return source;
}
/**
* 水印内发光(增加阴影)
*
* @param g2
* @param shadowWidth
* @param clipShape
*/
public void handler(Graphics2D g2, int shadowWidth, Shape clipShape) {
int gw = shadowWidth * 8;
for (int i = gw; i >= 2; i -= 2) {
float pct = (float) (gw - i) / (gw - 1);
Color mixHi = getMixedColor(OUTER_SHADOW_COLOR, pct, INNER_SHADOW_COLOR, 1.0f - pct);
Color mixLo = getMixedColor(OUTER_SHADOW_COLOR, pct, INNER_SHADOW_COLOR, 1.0f - pct);
g2.setPaint(new GradientPaint(0.0f, 35 * 0.25f, mixHi, 0, 35, mixLo));
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, pct));
g2.setStroke(new BasicStroke(i));
g2.draw(clipShape);
}
}
/**
* 处理水印白边情况
*
* @param g2
* @param shadowWidth
* @param clipShape
*/
private void shadowHandle(Graphics2D g2, int shadowWidth, Shape clipShape) {
g2.setColor(Color.black);
g2.setStroke(new BasicStroke(1));
g2.draw(clipShape);
}
/**
* 水印边框描白
*
* @param g2
* @param shadowWidth
* @param clipShape
*/
private void shadowHandleBlack(Graphics2D g2, int shadowWidth, Shape clipShape) {
g2.setColor(WATER_MARK_PADDING_COLOR);
g2.setStroke(new BasicStroke(1));
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1F));
g2.draw(clipShape);
}
上面代码还增加一些处理阴影图效果的方法的代码
萝卜青菜各有所爱,自己想要什么样的自己调去。
总结
全部代码放在这里(需求各位客官动动小手登录之后才能看到)👇
https://gitee.com/TyroneMax/captcha?_from=gitee_search
仓库里面包含了滑动验证码的校验,具体实现还有一个前端demo
写在最后
当然啦,登山的路有很多,这只是其中一条。如果以后我能找到更好的方案,也会再share一下。
我仍然是废物,请多指教!
原文始发于微信公众号(Hephaestuses):手把手教你写滑动图片验证码!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/45047.html