教育类的网站案例,wordpress 界面优化,inovation wordpress,360网站seo手机优化软件前言
现在大部分网部都是图片滑块验证码#xff0c;这个得要与后端联动起来才是确保接口安全性 通过我们系统在发送手机短息时都会选进行滑块验证#xff0c;但是我们要保证发送短息接口的全安#xff0c;具体路思如下 那么这个滑块的必须是与后端交互才能保证安全性…前言
现在大部分网部都是图片滑块验证码这个得要与后端联动起来才是确保接口安全性 通过我们系统在发送手机短息时都会选进行滑块验证但是我们要保证发送短息接口的全安具体路思如下 那么这个滑块的必须是与后端交互才能保证安全性而不是前端简单的交互。我们一起来学习一下这个案例怎么实吧 1、验证通过效果如图 2、验证失败效果如图
案例开始
1、我们使用java新建一个springboot工程并准备几张图片尺寸是390*180,如下图 添加依赖
dependencygroupIdnet.coobird/groupIdartifactIdthumbnailator/artifactIdversion0.4.11/version
/dependency2、新建三个响应的类 2.1 WebReturn类如下
Data
public class WebReturn {RetCode code;Object data;public WebReturn(RetCode code, Object data) {this.code code;this.data data;}
}2.2 RetCode类如下 public enum RetCode {IMAGE_REQ_SUCCESS(1,图片请求成功),IMAGE_REQ_FAIL(2,图片请求失败),VERIFI_REQ_SUCCESS(3,图片验证成功),VERIFI_REQ_FAIL(4,图片验证失败);int code;String message;RetCode(int code, String message) {this.code code;this.message message;}public int getCode() {return code;}public void setCode(int code) {this.code code;}public String getMessage() {return message;}public void setMessage(String message) {this.message message;}
}2.3 ImageResult类如下
Data
public class ImageResult {int xpos;//滑块的坐标x轴int ypos;//滑块的坐标y轴int cutImageWidth;//滑块的宽int cutImageHeight;//滑块的高String cutImage;//滑块图片String oriImage;//背景图初扣掉滑块的图}3、新建一个图片滑块生成的工具类
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
Slf4j
public class ImgUtil {//图片的路径private String classpath classpath*:img/slider/*.*;//图片的最大大小 (可以根据实际需要进行高调整对应的图片尺寸也得是一致)private static int IMAGE_MAX_WIDTH 380;private static int IMAGE_MAX_HEIGHT 190;//抠图上面的半径private static int RADIUS IMAGE_MAX_WIDTH/38;//抠图区域的高度private static int CUT_HEIGHT IMAGE_MAX_WIDTH/7;//抠图区域的宽度private static int CUT_WIDTH IMAGE_MAX_WIDTH/7;//被扣地方填充的颜色private static int FLAG 0xffffff;//抠图部分凸起的方向private Location location;ImageResult imageResult new ImageResult();private String ORI_IMAGE_KEY ORI_IMAGE_KEY;private String CUT_IMAGE_KEY CUT_IMAGE_KEY;private int XPOS;private int YPOS;Dataprivate class ImageMessage{private int xpos;private int ypos;private int cutImageWidth;private int cutImageHeight;}ImageMessage imageMessage new ImageMessage();/***功能描述 获取抠图区的坐标原点*/public void createXYPos(BufferedImage oriImage){int height oriImage.getHeight();int width oriImage.getWidth();XPOS new Random().nextInt(width-CUT_WIDTH-RADIUS);YPOS new Random().nextInt(height-CUT_HEIGHT-RADIUS-RADIUS)RADIUS;//确保横坐标位于2/43/4int div (IMAGE_MAX_WIDTH/4);if(XPOS/div 0 ){XPOS XPOS div*2;}else if(XPOS/div 1 ){XPOS XPOS div;}else if(XPOS/div 3 ){XPOS XPOS - div;}}/***功能描述 对外提供的接口*/public ImageResult imageResult() throws IOException{return imageResult(getRandomImage());}public ImageResult imageResult(BufferedImage oriBufferedImage) throws IOException {//检测图片大小oriBufferedImage checkImage(oriBufferedImage);//初始化原点坐标createXYPos(oriBufferedImage);//获取被扣图像的标志图int[][] blockData getBlockData(oriBufferedImage);//printBlockData(blockData);//计算抠图区域的信息createImageMessage();//获取扣了图的原图和被扣部分的图MapString,BufferedImage imageMap cutByTemplate(oriBufferedImage,blockData);imageResult.setOriImage(ImageBase64(imageMap.get(ORI_IMAGE_KEY)));imageResult.setCutImage(ImageBase64(imageMap.get(CUT_IMAGE_KEY)));imageResult.setXpos(imageMessage.getXpos());imageResult.setYpos(imageMessage.getYpos());imageResult.setCutImageWidth(imageMessage.getCutImageWidth());imageResult.setCutImageHeight(imageMessage.getCutImageHeight());return imageResult;}/***功能描述* Description 计算抠图的相关参数*/private void createImageMessage(){int x 0,y 0;int w 0, h 0;if(location Location.UP){x XPOS;y YPOS - RADIUS;w CUT_WIDTH;h CUT_HEIGHT RADIUS;}else if(location Location.LEFT){x XPOS-RADIUS;y YPOS;w CUT_WIDTH RADIUS;h CUT_HEIGHT;}else if(location Location.DOWN){x XPOS;y YPOS;w CUT_WIDTH;h CUT_HEIGHT RADIUS;}else if(location Location.RIGHT){x XPOS;y YPOS;w CUT_WIDTH RADIUS;h CUT_HEIGHT;}imageMessage.setXpos(x);imageMessage.setYpos(y);imageMessage.setCutImageHeight(h);imageMessage.setCutImageWidth(w);}/***功能描述* Description 检测图片大小是否符合要求*/private BufferedImage checkImage(BufferedImage image) throws IOException {if((image.getWidth() IMAGE_MAX_WIDTH) || (image.getHeight() IMAGE_MAX_HEIGHT)){return image;}else if((image.getWidth() IMAGE_MAX_WIDTH) || (image.getHeight() IMAGE_MAX_HEIGHT)){log.info(图片太小不符合要求w*h[380*190]);throw new IllegalArgumentException(图片太小不符合要求w*h[380*190]);} else {log.info(压缩图片);return compressImage(image,IMAGE_MAX_WIDTH,IMAGE_MAX_HEIGHT);}}private Color color(int rgb){int b (0xff rgb);int g (0xff (rgb 8));int r (0xff (rgb 16));return new Color(r, g, b);}/***功能描述 获取抠完图的原图和被扣出来的图*/public MapString,BufferedImage cutByTemplate(BufferedImage oriImage, int[][] blockData){MapString,BufferedImage imgMap new HashMap();BufferedImage cutImage new BufferedImage(imageMessage.cutImageWidth,imageMessage.cutImageHeight,oriImage.getType());// 获取Graphics2DGraphics2D g2d cutImage.createGraphics();//透明化整张图cutImage g2d.getDeviceConfiguration().createCompatibleImage(imageMessage.cutImageWidth,imageMessage.cutImageHeight, Transparency.BITMASK);g2d.dispose();g2d cutImage.createGraphics();// 背景透明代码结束int xmax imageMessage.xpos imageMessage.cutImageWidth;int ymax imageMessage.ypos imageMessage.cutImageHeight;for(int x imageMessage.xpos; x xmax x0; x){for(int y imageMessage.ypos; y ymax y0; y){int oriRgb oriImage.getRGB(x,y);if(blockData[x][y] FLAG){oriImage.setRGB(x,y,FLAG);//描边 判断是否为边界如果是边界则填充为白色if(blockData[x-1][y] ! FLAG || blockData[x1][y] ! FLAG || blockData[x][y1] ! FLAG || blockData[x][y-1] ! FLAG){g2d.setColor(color(0xffffff));}else{g2d.setColor(color(oriRgb));}g2d.setStroke(new BasicStroke(1f));g2d.fillRect(x-imageMessage.xpos, y-imageMessage.ypos, 1, 1);}}}// 释放对象g2d.dispose();imgMap.put(ORI_IMAGE_KEY,oriImage);imgMap.put(CUT_IMAGE_KEY,cutImage);return imgMap;}/***功能描述* Description 获取抠图数据被扣的像素点将使用FLAG进行标记* return: int[][]*/public int[][] getBlockData(BufferedImage oriImage){int height oriImage.getHeight();int width oriImage.getWidth();int[][] blockData new int[width][height];Location locations[] {Location.UP,Location.LEFT,Location.DOWN,Location.RIGHT};//矩形//此处还可以优化进行区域扫描for(int x 0; x width x0; x){for(int y 0; y height y0; y){blockData[x][y] 0;if ( (x XPOS) (x (XPOSCUT_WIDTH)) (y YPOS) (y (YPOSCUT_HEIGHT))){blockData[x][y] FLAG;}}}//圆形突出区域//突出圆形的原点坐标(x,y)int xBulgeCenter0,yBulgeCenter0;int xConcaveCenter0,yConcaveCenter0;//位于矩形的哪一边0123--上下左右location locations[new Random().nextInt(4)];if(location Location.UP){//上 凸起xBulgeCenter XPOS CUT_WIDTH/2;yBulgeCenter YPOS;//左 凹陷xConcaveCenter XPOS ;yConcaveCenter YPOS CUT_HEIGHT/2;}else if(location Location.DOWN){//下 凸起xBulgeCenter XPOS CUT_WIDTH/2;yBulgeCenter YPOS CUT_HEIGHT;//右 凹陷xConcaveCenter XPOS CUT_WIDTH;yConcaveCenter YPOS CUT_HEIGHT/2;}else if(location Location.LEFT){//左 凸起xBulgeCenter XPOS ;yBulgeCenter YPOS CUT_HEIGHT/2;//下 凹陷xConcaveCenter XPOS CUT_WIDTH/2;yConcaveCenter YPOS CUT_HEIGHT;}else {//Location.RIGHT//右 凸起xBulgeCenter XPOS CUT_WIDTH;yBulgeCenter YPOS CUT_HEIGHT/2;//上 凹陷xConcaveCenter XPOS CUT_WIDTH/2;yConcaveCenter YPOS;}//半径的平方int RADIUS_POW2 RADIUS * RADIUS;//凸起部分for(int x xBulgeCenter-RADIUS; x xBulgeCenterRADIUS x0; x){for(int y yBulgeCenter-RADIUS; y yBulgeCenterRADIUS y0; y){if(Math.pow((x-xBulgeCenter),2) Math.pow((y-yBulgeCenter),2) RADIUS_POW2){blockData[x][y] FLAG;}}}//凹陷部分for(int x xConcaveCenter-RADIUS; x xConcaveCenterRADIUS x0; x){for(int y yConcaveCenter-RADIUS; y yConcaveCenterRADIUS y0; y){if(Math.pow((x-xConcaveCenter),2) Math.pow((y-yConcaveCenter),2) RADIUS_POW2){blockData[x][y] 0;}}}return blockData;}/***功能描述 将图片转为base64存储*/private String ImageBase64(BufferedImage bufferedImage) throws IOException {ByteArrayOutputStream out new ByteArrayOutputStream();ImageIO.write(bufferedImage, png, out);//转成byte数组byte[] bytes out.toByteArray();Base64.Encoder encoder Base64.getEncoder();//生成BASE64编码return encoder.encodeToString(bytes);}/*** 随机获取一个图片文件* return* throws Exception*/private BufferedImage getRandomImage() throws IOException {try {//使用resource获取resource文件【注意即使打成jar包也有效】ResourcePatternResolver resourcePatternResolver new PathMatchingResourcePatternResolver();Resource[] resources resourcePatternResolver.getResources(classpath);if (resources.length 0) {throw new IOException(该文件夹内没有文件);} else {int index new Random().nextInt(resources.length);InputStream inputStream resources[index].getInputStream();BufferedImage marioBufferedImage ImageIO.read(inputStream);return marioBufferedImage;}} catch (IOException e) {log.info(读取文件失败{}, e);throw new IOException(读取文件失败);}}/***功能描述 压缩图片* author lgj* Description* date 3/30/20* param:* return: java.awt.image.BufferedImage**/private BufferedImage compressImage(BufferedImage image,int width,int height) throws IOException{return Thumbnails.of(image).forceSize(width,height)//.width(width).height(height).asBufferedImage();}/***功能描述* Description 抠图部分凸起的区域*/private enum Location {UP,LEFT,DOWN,RIGHT;}
}4、新建一个controller Slf4j
RestController
RequestMapping(/slider)
public class SliderController {private int xPosCache 0;//生产环境请把这个值存入redis中RequestMapping(/image)public WebReturn image(){ImageResult imageResult null;try{imageResult new ImgUtil().imageResult();//生成图片xPosCache imageResult.getXpos();//生产环境请把这个值存入redis中imageResult.setXpos(0);//清空x值return new WebReturn(RetCode.IMAGE_REQ_SUCCESS,imageResult);}catch(Exception ex){log.error(ex.getMessage());ex.printStackTrace();return new WebReturn(RetCode.IMAGE_REQ_FAIL,null);}}RequestMapping(/verification)public WebReturn verification(RequestParam(moveX) int moveX){log.info(/slider/verification/{},moveX);int MOVE_CHECK_ERROR 2;//允许的误差范围这里设置为2个像素//xPosCache 生产请从redis中读取使用完并立即请除if(( moveX ( xPosCache MOVE_CHECK_ERROR)) ( moveX (xPosCache - MOVE_CHECK_ERROR))){log.info(验证正确);//生产这个可以返回临时授权码return new WebReturn(RetCode.VERIFI_REQ_SUCCESS,true);}return new WebReturn(RetCode.VERIFI_REQ_FAIL,false);}
}5、最后新建一个页面
!DOCTYPE html
html langen
headmeta charsetUTF-8title首页/titlescript srchttps://cdn.bootcss.com/jquery/3.4.1/jquery.js/scriptstyle html body {height: 100%;width: 100%;}#captchaContainer{position: absolute;top: 50px;left: 40%;/* background-color: #f57a7a;*/height: 275px;width: 260px;}.header{position: absolute;top: 0px;left: 0px;background-color: rgb(245, 236, 236);height: 40px;width: 380px;}.headerText{position: absolute;top: 13px;left: 140px;height: 40px;color:#66c523;font:18px/14px Georgia, Times New Roman, Times, serif;}#captchaImg{position: absolute;left: 0;top: 40px;height: 190px;width: 380px;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;border: none}#oriImg{position: absolute;left: 0px;top: 0px;width: 380px;height: 190px;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;border: none}#cutImg{position: absolute;border: none;left: 0px;}.sliderContainer {position: absolute;bottom: 0;left: 0px;text-align: center;width: 380px;height: 40px;line-height: 40px;background: #f7f9fa;color: #45494c;border: 1px solid #e4e7eb;}.sliderContainer_success{border: 1px solid hsl(125, 93%, 44%);}.sliderContainer_fail{border: 1px solid #ec3655;}.slider {position: absolute;top: 0;left: 0px;width: 40px;height: 40px;background: #fff;box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);transition: background .2s linear;cursor: pointer;cursor: grab;}.slider_success{border: 1px solid hsl(125, 93%, 44%);}.slider_fail{border: 1px solid #ec3655;}.sliderContainer_active .slider {height: 38px;top: -1px;border: 1px solid #1991FA;}.sliderContainer_active .sliderMask {height: 38px;border-width: 1px;}.sliderContainer_success .slider {height: 38px;top: -1px;margin-left: -1px;border: 1px solid #52CCBA;background-color: #52CCBA !important;}.sliderContainer_success .sliderMask {height: 38px;border: 1px solid #52CCBA;background-color: #D2F4EF;}.sliderContainer_success .sliderIcon {background-position: 0 0 !important;}.sliderContainer_fail .slider {height: 38px;top: -1px;border: 1px solid #f57a7a;background-color: #f57a7a !important;}.sliderContainer_fail .sliderMask {height: 38px;border: 1px solid #f57a7a;background-color: #fce1e1;}.sliderContainer_fail .sliderIcon {top: 14px;background-position: 0 -82px !important;}.sliderContainer_active .sliderText, .sliderContainer_success .sliderText, .sliderContainer_fail .sliderText {display: none;}.sliderMask {position: absolute;left: 0;top: 0;height: 40px;border: 0 solid #1991FA;background: #D1E9FE;}.slider:active {cursor: grabbing;}.slider:hover {background: #1991FA;}.slider:hover .sliderIcon {background-position: 0 -13px;}.sliderIcon {position: absolute;top: 15px;left: 13px;width: 14px;height: 12px;background: url(http://cstaticdun.126.net//2.6.3/images/icon_light.f13cff3.png) 0 -26px;background-size: 34px 471px;}.refreshIcon {position: absolute;right: 0;top: 0;width: 34px;height: 34px;cursor: pointer;background: url(http://cstaticdun.126.net//2.6.3/images/icon_light.f13cff3.png) 0 -437px;background-size: 34px 471px;}/style/head
bodydiv idcaptchaContainer!-- 标题栏 --div classheaderspan classheaderText图片滑动验证/spanspan classrefreshIcon//div !-- 图片显示区域 --div idcaptchaImgimg idoriImg srcdd alt原图/img idcutImg srcsa alt抠图//div!--滑块显示区域--div classsliderContainerdiv classsliderMaskdiv classsliderspan classsliderIcon/span/div/divspan classsliderText向右滑动填充拼图/span/div /div/bodyscript//图片显示使用base64时的前缀,srcbase64PrefixPath imgBase64Valuevar base64PrefixPathdata:image/png;base64,;var IMAGE_WIDTH 380;//初始化//滑块初始偏移量var sliderInitOffset 0;//滑块移动的最值var MIN_MOVE 0;var MAX_MOVE 0;//鼠标按下标志var mousedownFlagfalse;//滑块移动的距离var moveX;//滑块位置检测允许的误差正负var MOVE_CHECK_ERROR 2;//滑块滑动使能var moveEnable true;var ImageMsg {//抠图的坐标xpos: 0,ypos: 0,//抠图的大小cutImageWidth: 0,cutImageHeight: 0,//原图的base64oriImageSrc: 0,//抠图的base64cutImageSrc: 0,}//加载页面时进行初始化function init(){console.log(init)moveEnable true;mousedownFlagfalse;$(.slider).css(left,0px);initClass();MAX_MOVE IMAGE_WIDTH - ImageMsg.cutImageWidth;console.log(ImageMsg ImageMsg)$(#cutImg).css(left,0px);$(#oriImg).attr(src,ImageMsg.oriImageSrc)$(#cutImg).attr(src,ImageMsg.cutImageSrc)$(#cutImg).css(width,ImageMsg.cutImageWidth)$(#cutImg).css(height,ImageMsg.cutImageHeight)$(#cutImg).css(top,ImageMsg.ypos)}//加载页面时$(function(){httpRequest.requestImage.request();})var httpRequest{//请求获取图片requestImage:{path: slider/image,request:function(){$.get(httpRequest.requestImage.path,function(data,status){console.log(data)console.log(data.message);if(data.data ! null){ImageMsg.oriImageSrc base64PrefixPath data.data.oriImage;ImageMsg.cutImageSrc base64PrefixPath data.data.cutImage;ImageMsg.xpos data.data.xpos;ImageMsg.ypos data.data.ypos;ImageMsg.cutImageWidth data.data.cutImageWidth;ImageMsg.cutImageHeight data.data.cutImageHeight;init();}});},},//请求验证requestVerification:{path: slider/verification,request:function(){$.get(httpRequest.requestVerification.path,{moveX:(moveX)},function(data,status){console.log(data)console.log(data.code);console.log(data.message);if(data.data true){checkSuccessHandle();}else{checkFailHandle();}});},},}//刷新图片操作$(.refreshIcon).on(click,function(){httpRequest.requestImage.request();})//滑块鼠标按下$(.slider).mousedown(function(event){console.log(鼠标按下mousedown:event.clientX event.clientY);sliderInitOffset event.clientX; mousedownFlag true;//滑块绑定鼠标滑动事件$(.slider).on(mousemove,function(event){if(mousedownFlag false){return;}if(moveEnable false){return}moveX event.clientX - sliderInitOffset;moveXMIN_MOVE?moveXMIN_MOVE:moveXmoveX;moveXMAX_MOVE?moveXMAX_MOVE:moveXmoveX;$(this).css(left,moveXpx);$(#cutImg).css(left,moveXpx);})})//滑块鼠标弹起操作$(.slider).mouseup(function(event){console.log(mouseup:event.clientX event.clientY);sliderInitOffset 0;$(this).off(mousemove);mousedownFlagfalse;console.log(moveX moveX)checkLocation();})//检测滑块 位置是否正确function checkLocation(){moveEnable false;//后端请求检测滑块位置httpRequest.requestVerification.request();}function checkSuccessHandle(){$(.sliderContainer).addClass(sliderContainer_success);$(.slider).addClass(slider_success);}function checkFailHandle(){$(.sliderContainer).addClass(sliderContainer_fail);$(.slider).addClass(slider_success);}function initClass(){$(.sliderContainer).removeClass(sliderContainer_success);$(.slider).removeClass(slider_success);$(.sliderContainer).removeClass(sliderContainer_fail);$(.slider).removeClass(slider_fail);}/script/html最后
到这里我们的案例代码已经写好了运程工程访问就可以了。 http://localhost:9005/home.html 本文章提供大家学习欢迎你大家留言提供您的保贵意见 如果本文对您有帮助麻烦您给博主点个赞吧