Java滑块验证码原理和实现

臭味相投的朋友们,我在这里:
猿in小站:http://www.yuanin.net
csdn博客:https://blog.csdn.net/jiabeis
简书:https://www.jianshu.com/u/4cb7d664ec4b
微信免费订阅号“猿in”
猿in

文章目录

    • 滑块验证码引入
    • 滑块验证码原理
    • 滑块验证码实现
    • 参考

滑块验证码引入

当前互联网流行使用滑块验证码,如下图是网易严选的登录验证部分。
Java滑块验证码原理和实现_第1张图片

滑块验证码原理

很多网站使用滑块验证码提高网站安全性,为了做到真正的验证,必须要走后台服务器。
下面是java实现滑块验证的核心步骤:

  1. 从服务器随机取一张图片,并对图片上的随机x,y坐标和宽高一块区域抠图;
  2. 根据步骤一的坐标和宽高,使用二维数组保存原图上抠图区域的像素点坐标;
  3. 根据步骤二的坐标点,对原图的抠图区域的颜色进行处理。
  4. 完成以上步骤之后得到两张图(扣下来的方块图,带有抠图区域阴影的原图),将这两张图和抠图区域的y坐标传到前台,前端在移动方块验证时,将移动后的x坐标传递到后台与原来的x坐标作比较,如果在阈值内则验证通过。
  5. 请求验证的步骤:前台向后台发起请求,后台随机一张图片做处理将处理完的两张图片的base64,抠图y坐标和token(token为后台缓存验证码的唯一token,可以用缓存和分布式缓存)返回给前台。
  6. 前台滑动图片将x坐标和token作为参数请求后台验证,服务器根据token取出x坐标与参数的x进行比较。

滑块验证码实现

  1. 说明
    实现是基于springmvc的实现,前端为freemarker模板。
  2. 工具类
package com.sikong.ms.web.shiro;


import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @Auther herb
 * @Description 本地缓存:用户图形验证码资源
 * @Date: create in ${Time} ${Date}
 * @Modified By: herb
 */
@Slf4j
public class ValidateCache {

    private static LoadingCache> cache = CacheBuilder.newBuilder()
            .maximumSize(2)
            .expireAfterWrite(365, TimeUnit.DAYS)
            .build(createCacheLoader());

    private static CacheLoader> createCacheLoader() {
        return new CacheLoader>(){
            @Override
            public List load(String key) throws Exception {
                return null;
            }
        };
    }

    public static void set(String key,List value){
        cache.put(key,value);
    }

    public static List get(String key){
        List value = null;
        try {
            value = cache.get(key);
        } catch (Exception e) {
            log.info("ValidateCache 缓存未查询到图片或模板!");
            value = null;
        }
        return value;
    }
    public static long size(){
        return cache.size();
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        List value = new ArrayList<>();
        value.add("ok".getBytes("utf-8"));
        ValidateCache.set("121", value);
        ValidateCache.set("111",value);
        ValidateCache.set("12",value);
        ValidateCache.set("113",value);
        System.out.println(cache.size());
        //注意缓存的最大容量
        List rv = ValidateCache.get("113");
        System.out.println(new String(rv.get(0),"utf-8"));
        System.out.println(ValidateCache.size());
        while (cache.size() > 0){
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            List rv1 = ValidateCache.get("113");
            System.out.println(new String(rv1.get(0),"utf-8"));
        }

    }
}

package com.sikong.ms.common.captcha.validate;

import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;

/**
 * 滑块验证工具类
 *
 * @author: herb
 * @Description:
 * @Date: Created in 10:57 2018/6/25
 * @Modified By:
 */
@Slf4j
public class VerifyImageUtil {

    private static int ORI_WIDTH = 350;  //源文件宽度
    private static int ORI_HEIGHT = 213;  //源文件高度
    private static int X;  //抠图坐标x
    private static int Y;  //抠图坐标y
    private static int WIDTH;  //模板图宽度
    private static int HEIGHT;  //模板图高度
    private static float xPercent;  //X位置移动百分比
    private static float yPercent;  //Y位置移动百分比

    public static int getX() {
        return X;
    }

    public static int getY() {
        return Y;
    }

    public static float getxPercent() {
        return xPercent;
    }

    public static float getyPercent() {
        return yPercent;
    }
    /**
     * 根据模板切图
     *
     * @param templateFile
     * @param targetFile
     * @param templateType
     * @param targetType
     * @return
     * @throws Exception
     */
    public static Map pictureTemplatesCut(byte[] templateFile, byte[] targetFile, String templateType, String targetType) throws Exception {
        Map pictureMap = new HashMap<>();
        // 文件类型
        String templateFiletype = templateType;
        String targetFiletype = targetType;
        if (StringUtils.isEmpty(templateFiletype) || StringUtils.isEmpty(targetFiletype)) {
            throw new RuntimeException("file type is empty");
        }
        // 源文件流
        //File Orifile = targetFile;
        //InputStream oriis = new FileInputStream(Orifile);

        // 模板图
        BufferedImage imageTemplate = ImageIO.read(new ByteArrayInputStream(templateFile));
        WIDTH = imageTemplate.getWidth();
        HEIGHT = imageTemplate.getHeight();
        // 模板图
        BufferedImage imageTarget = ImageIO.read(new ByteArrayInputStream(targetFile));
        ORI_WIDTH = imageTarget.getWidth();
        ORI_HEIGHT = imageTarget.getHeight();

        generateCutoutCoordinates();
        // 最终图像
        BufferedImage newImage = new BufferedImage(WIDTH, HEIGHT, imageTemplate.getType());
        Graphics2D graphics = newImage.createGraphics();
        graphics.setBackground(Color.white);

        int bold = 5;
        // 获取感兴趣的目标区域
        BufferedImage targetImageNoDeal = getTargetArea(X, Y, WIDTH, HEIGHT, new ByteArrayInputStream(targetFile), targetFiletype);


        // 根据模板图片抠图
        newImage = DealCutPictureByTemplate(targetImageNoDeal, imageTemplate, newImage);

        // 设置“抗锯齿”的属性
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setStroke(new BasicStroke(bold, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        graphics.drawImage(newImage, 0, 0, null);
        graphics.dispose();

        ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。
        ImageIO.write(newImage, templateFiletype, os);//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。
        byte[] newImages = os.toByteArray();
        pictureMap.put("newImage", newImages);
        // 源图生成遮罩
        BufferedImage oriImage = ImageIO.read(new ByteArrayInputStream(targetFile));
        byte[] oriCopyImages = DealOriPictureByTemplate(oriImage, imageTemplate, X, Y);
        pictureMap.put("oriCopyImage", oriCopyImages);
        pictureMap.put("X",X);
        pictureMap.put("Y",Y);
        return pictureMap;
    }

    /**
     * 根据模板切图
     *
     * @param templateFile
     * @param targetFile
     * @param templateType
     * @param targetType
     * @return
     * @throws Exception
     */
    public static Map pictureTemplatesCut(File templateFile, File targetFile, String templateType, String targetType) throws Exception {
        Map pictureMap = new HashMap<>();
        // 文件类型
        String templateFiletype = templateType;
        String targetFiletype = targetType;
        if (StringUtils.isEmpty(templateFiletype) || StringUtils.isEmpty(targetFiletype)) {
            throw new RuntimeException("file type is empty");
        }
        // 源文件流
        File Orifile = targetFile;
        InputStream oriis = new FileInputStream(Orifile);

        // 模板图
        BufferedImage imageTemplate = ImageIO.read(templateFile);
        WIDTH = imageTemplate.getWidth();
        HEIGHT = imageTemplate.getHeight();
        // 模板图
        BufferedImage imageTarget = ImageIO.read(Orifile);
        ORI_WIDTH = imageTarget.getWidth();
        ORI_HEIGHT = imageTarget.getHeight();

        generateCutoutCoordinates();
        // 最终图像
        BufferedImage newImage = new BufferedImage(WIDTH, HEIGHT, imageTemplate.getType());
        Graphics2D graphics = newImage.createGraphics();
        graphics.setBackground(Color.white);

        int bold = 5;
        // 获取感兴趣的目标区域
        BufferedImage targetImageNoDeal = getTargetArea(X, Y, WIDTH, HEIGHT, oriis, targetFiletype);


        // 根据模板图片抠图
        newImage = DealCutPictureByTemplate(targetImageNoDeal, imageTemplate, newImage);

        // 设置“抗锯齿”的属性
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setStroke(new BasicStroke(bold, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        graphics.drawImage(newImage, 0, 0, null);
        graphics.dispose();

        ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。
        ImageIO.write(newImage, templateFiletype, os);//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。
        byte[] newImages = os.toByteArray();
        pictureMap.put("newImage", newImages);
        // 源图生成遮罩
        BufferedImage oriImage = ImageIO.read(Orifile);
        byte[] oriCopyImages = DealOriPictureByTemplate(oriImage, imageTemplate, X, Y);
        pictureMap.put("oriCopyImage", oriCopyImages);
        pictureMap.put("X",X);
        pictureMap.put("Y",Y);
        return pictureMap;
    }

    /**
     * 抠图后原图生成
     *
     * @param oriImage
     * @param templateImage
     * @param x
     * @param y
     * @return
     * @throws Exception
     */
    private static byte[] DealOriPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage, int x,
                                                   int y) throws Exception {
        // 源文件备份图像矩阵 支持alpha通道的rgb图像
        BufferedImage ori_copy_image = new BufferedImage(oriImage.getWidth(), oriImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        // 源文件图像矩阵
        int[][] oriImageData = getData(oriImage);
        // 模板图像矩阵
        int[][] templateImageData = getData(templateImage);

        //copy 源图做不透明处理
        for (int i = 0; i < oriImageData.length; i++) {
            for (int j = 0; j < oriImageData[0].length; j++) {
                int rgb = oriImage.getRGB(i, j);
                int r = (0xff & rgb);
                int g = (0xff & (rgb >> 8));
                int b = (0xff & (rgb >> 16));
                //无透明处理
                rgb = r + (g << 8) + (b << 16) + (255 << 24);
                ori_copy_image.setRGB(i, j, rgb);
            }
        }

        for (int i = 0; i < templateImageData.length; i++) {
            for (int j = 0; j < templateImageData[0].length - 5; j++) {
                int rgb = templateImage.getRGB(i, j);
                //对源文件备份图像(x+i,y+j)坐标点进行透明处理
                if (rgb != 16777215 && rgb <= 0) {
                    int rgb_ori = ori_copy_image.getRGB(x + i, y + j);
                    int r = (0xff & rgb_ori);
                    int g = (0xff & (rgb_ori >> 8));
                    int b = (0xff & (rgb_ori >> 16));
                    rgb_ori = r + (g << 8) + (b << 16) + (150 << 24);
                    ori_copy_image.setRGB(x + i, y + j, rgb_ori);
                } else {
                    //do nothing
                }
            }
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。
        ImageIO.write(ori_copy_image, "png", os);//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。
        byte b[] = os.toByteArray();//从流中获取数据数组。
        return b;
    }


    /**
     * 根据模板图片抠图
     *
     * @param oriImage
     * @param templateImage
     * @return
     */

    private static BufferedImage DealCutPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage,
                                                          BufferedImage targetImage) throws Exception {
        // 源文件图像矩阵
        int[][] oriImageData = getData(oriImage);
        // 模板图像矩阵
        int[][] templateImageData = getData(templateImage);
        // 模板图像宽度
        try {
            for (int i = 0; i < templateImageData.length; i++) {
                // 模板图片高度
                for (int j = 0; j < templateImageData[0].length; j++) {
                    // 如果模板图像当前像素点不是白色 copy源文件信息到目标图片中
                    int rgb = templateImageData[i][j];
                    if (rgb != 16777215 && rgb <= 0) {
                        targetImage.setRGB(i, j, oriImageData[i][j]);
                    }
                }
            }
        } catch (ArrayIndexOutOfBoundsException e) {/*数组越界错误处理,这样页面就不会返回图像问题。*/
            log.error("X:"+X+ "||Y:"+Y,e);
        } catch (Exception e) {
            log.error("X:"+X+ "||Y:"+Y,e);
        }
        return targetImage;
    }


    /**
     * 获取目标区域
     *
     * @param x            随机切图坐标x轴位置
     * @param y            随机切图坐标y轴位置
     * @param targetWidth  切图后目标宽度
     * @param targetHeight 切图后目标高度
     * @param ois          源文件输入流
     * @return
     * @throws Exception
     */
    private static BufferedImage getTargetArea(int x, int y, int targetWidth, int targetHeight, InputStream ois,
                                               String filetype) throws Exception {
        Iterator imageReaderList = ImageIO.getImageReadersByFormatName(filetype);
        ImageReader imageReader = imageReaderList.next();
        // 获取图片流
        ImageInputStream iis = ImageIO.createImageInputStream(ois);
        // 输入源中的图像将只按顺序读取
        imageReader.setInput(iis, true);

        ImageReadParam param = imageReader.getDefaultReadParam();
        Rectangle rec = new Rectangle(x, y, targetWidth, targetHeight);
        param.setSourceRegion(rec);
        BufferedImage targetImage = imageReader.read(0, param);
        return targetImage;
    }

    /**
     * 生成图像矩阵
     *
     * @param
     * @return
     * @throws Exception
     */
    private static int[][] getData(BufferedImage bimg) throws Exception {
        int[][] data = new int[bimg.getWidth()][bimg.getHeight()];
        for (int i = 0; i < bimg.getWidth(); i++) {
            for (int j = 0; j < bimg.getHeight(); j++) {
                data[i][j] = bimg.getRGB(i, j);
            }
        }
        return data;
    }

    /**
     * 随机生成抠图坐标
     */
    private static void generateCutoutCoordinates() {
        Random random = new Random();
        int widthDifference = ORI_WIDTH - WIDTH;
        int heightDifference = ORI_HEIGHT - HEIGHT;

        if (widthDifference <= 0) {
            X = 5;

        } else {
            X = random.nextInt(ORI_WIDTH - WIDTH);
            if (X < WIDTH) {/*@herb 解决切图相对位置问题*/
                X = WIDTH;
            }
        }

        if (heightDifference <= 0) {
            Y = 5;
        } else {
            Y = random.nextInt(ORI_HEIGHT - HEIGHT);
        }
        NumberFormat numberFormat = NumberFormat.getInstance();
        numberFormat.setMaximumFractionDigits(2);

        xPercent = Float.parseFloat(numberFormat.format((float) X / (float) ORI_WIDTH));
        yPercent = Float.parseFloat(numberFormat.format((float) Y / (float) ORI_HEIGHT));
    }
}



  1. controller类
package com.sikong.ms.web.controller;

import com.google.common.collect.Maps;
import com.sikong.hexuan.entity.welfare.WelEmployee;
import com.sikong.ms.common.auth.shiro.UsernamePasswordToken;
import com.sikong.ms.common.cache.jedis.JedisConfig;
import com.sikong.ms.common.cache.jedis.JedisUtils;
import com.sikong.ms.common.captcha.validate.VerifyImageUtil;
import com.sikong.ms.common.util.Digests;
import com.sikong.ms.common.util.FileUtil;
import com.sikong.ms.web.service.WelEmployeeService;
import com.sikong.ms.web.shiro.AccountAuthorizationRealm;
import com.sikong.ms.web.shiro.CacheUtils;
import com.sikong.ms.web.shiro.ValidateCache;
import com.sikong.ms.web.utils.NumberUtils;
import com.sikong.ms.web.utils.SendMessageUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Controller;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import java.io.*;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;


/**
 1. Created by yansheng on 2014/6/29.
 */
@Slf4j
@Controller
@RequestMapping("/validate")
public class ValidateController {
    @Resource
    private WelEmployeeService employeeService;
    /**
     * 生成验证码
     *
     * @return
     */
    @RequestMapping(value = "/init")
    @ResponseBody
    public JSONObject init() throws IOException {
        JSONObject object = new JSONObject();

        /*redis实现:使用base64编码转化成字符串处理*/
//        List imgList = JedisUtils.getList(JedisConfig.KEY_VALIDATE_IMG);
//        List tpllist = JedisUtils.getList(JedisConfig.KEY_VALIDATE_TPL);
//        if (null == imgList || imgList.size() < 1 || tpllist == null || tpllist.size() < 1) {
//            imgList = new ArrayList();
//            tpllist = new ArrayList();
//            initValidateResources(imgList,tpllist);
//            JedisUtils.setList(JedisConfig.KEY_VALIDATE_IMG,imgList,JedisConfig.JEDIS_EXPIRE*3);
//            JedisUtils.setList(JedisConfig.KEY_VALIDATE_TPL,tpllist,JedisConfig.JEDIS_EXPIRE*3);
//        }

        /*本地缓存实现*/
        List imgList = ValidateCache.get(JedisConfig.KEY_VALIDATE_IMG);
        List tpllist = ValidateCache.get(JedisConfig.KEY_VALIDATE_TPL);
        if (null == imgList || imgList.size() < 1 || tpllist == null || tpllist.size() < 1) {
            imgList = new ArrayList();
            tpllist = new ArrayList();
            initValidateResources(imgList,tpllist);
            ValidateCache.set(JedisConfig.KEY_VALIDATE_IMG,imgList);
            ValidateCache.set(JedisConfig.KEY_VALIDATE_TPL,tpllist);
        }

        byte[] targetIS = null;
        byte[] templateIS = null;
        Random ra = new Random();
        if (null != imgList){
            int rd = ra.nextInt(imgList.size());
            targetIS = imgList.get(rd);
        }
        if (null != tpllist){
            int rd = ra.nextInt(tpllist.size());
            templateIS = tpllist.get(rd);
        }

        Map pictureMap = null;
        try {
            pictureMap = VerifyImageUtil.pictureTemplatesCut(templateIS,targetIS , "png", "jpg");
            String newImage = Base64Utils.encodeToString((byte[]) pictureMap.get("newImage"));
            String sourceImage = Base64Utils.encodeToString((byte[]) pictureMap.get("oriCopyImage"));
            int X = (int) pictureMap.get("X");
            int Y = (int) pictureMap.get("Y");
            object.put("newImage", newImage);
            object.put("sourceImage", sourceImage);
            //object.put("X", X);
            object.put("Y", Y);


            String token = UUID.randomUUID().toString().replaceAll("-", "");
            Map tokenObj = new HashMap();
            tokenObj.put("token", token);
            tokenObj.put("X", X);
            tokenObj.put("Y", Y);
            //token 保存2分钟
            JedisUtils.setObjectMap(JedisConfig.KEY_VALIDATE_TOKEN + ":" + token, tokenObj, 120000);
            object.put("token", token);
        } catch (Exception e) {
            log.error("",e);
        }
        return object;
    }

    /**
     * 初始化验证图形生成资源
     * @param imgList
     * @param tpllist
     */
    private void initValidateResources(List imgList, List tpllist) throws IOException {
        /*加载验证原图*/
        String target = URLDecoder.decode(ValidateController.class.getClassLoader().getResource("static/image/validate/targets").getPath(),"UTF-8");
        byte[] targetIS = null;
        byte[] templateIS = null;
        if (target.indexOf("!/") != -1) {//jar包
            String jarPath = "jar:" + target;
            log.debug(jarPath);
            URL jarURL = new URL(jarPath);
            JarURLConnection jarCon = (JarURLConnection) jarURL.openConnection();
            JarFile jarFile = jarCon.getJarFile();
            Enumeration jarEntrys = jarFile.entries();
            while (jarEntrys.hasMoreElements()) {
                JarEntry entry = jarEntrys.nextElement();
                String name = entry.getName();
                if (name.startsWith("static/image/validate/targets") && !name.equals("static/image/validate/targets/") && (name.endsWith(".jpg") || name.endsWith(".png"))) {
                    log.debug("targets=" + name);
                    InputStream isTemplates = jarFile.getInputStream(entry);
                    targetIS = IOUtils.toByteArray(jarFile.getInputStream(entry));
                    imgList.add(targetIS);

                } else if (name.startsWith("static/image/validate/templates") && !name.equals("static/image/validate/templates/")  && (name.endsWith(".jpg") || name.endsWith(".png"))) {
                    log.debug("templates=" + name);
                    InputStream isTemplates = jarFile.getInputStream(entry);
                    templateIS = IOUtils.toByteArray(jarFile.getInputStream(entry));
                    tpllist.add(templateIS);
                }
            }
        } else {
            File targetBaseFile = new File(target);
            if (null != targetBaseFile) {
                File[] fs = targetBaseFile.listFiles();
//                Random ra = new Random();
//                if (null != fs && fs.length > 0) {
//                    int random = ra.nextInt(fs.length);
//                    targetIS = IOUtils.toByteArray(new FileInputStream(fs[random]));
//                }
                for (File f : fs){
                    targetIS = IOUtils.toByteArray(new FileInputStream(f));
                    imgList.add(targetIS);
                }
            }
            /*加载切图模板*/
            String template = URLDecoder.decode(ValidateController.class.getClassLoader().getResource("static/image/validate/templates").getFile(),"UTF-8");
            File templateBaseFile = new File(template);
            if (null != templateBaseFile) {
                File[] fs = templateBaseFile.listFiles();
//                Random ra = new Random();
//                if (null != fs && fs.length > 0) {
//                    int random = ra.nextInt(fs.length);
//                    templateIS = IOUtils.toByteArray(new FileInputStream(fs[random]));
//                }
                for (File f : fs){
                    templateIS = IOUtils.toByteArray(new FileInputStream(f));
                    tpllist.add(templateIS);
                }
            }
        }
        log.info("initValidateResources:template size:" + tpllist.size() + "target size:" + imgList.size());
    }


    /**
     * 验证方法 (有验证码的方法提交,有时候也可以带上验证参数,做后端二次验证)
     *
     * @return
     */
    @RequestMapping(value = "check",method = RequestMethod.POST)
    @ResponseBody
    public boolean check(String token, int X, int Y) {
        JSONObject message = new JSONObject();
        message.put("code", 2);
        message.put("massage", "验证不通过,请重试!");
        if (null == token || token.trim().length() < 1) {
            message.put("code", 0);
            message.put("massage", "请求参数错误:token为空!");
        }
        Map tokenObj = JedisUtils.getObjectMap(JedisConfig.KEY_VALIDATE_TOKEN + ":" + token);
        if (null == tokenObj) {
            message.put("code", -1);
            message.put("massage", "验证码超期,请重新请求!");
        } else {
            int sX = (Integer) tokenObj.get("X");
            int sY = (Integer) tokenObj.get("Y");
            if (sY != Y) {
                message.put("code", 0);
                message.put("massage", "请求参数错误:位置信息不正确!");
            } else {
                if (Math.abs(sX - X) <= 2) {
                    message.put("code", 1);
                    message.put("massage", "验证通过!");
                } else {
                    message.put("code", 2);
                    message.put("massage", "验证不通过,请重试!");
                }
            }
        }
        if (message.get("code")==1) {
            return true;
        } else {
            return false;
        }
    }



    /**
     * 根据手机号查询用户是否存在
     */

    @RequestMapping("/checkUserIsHave")
    @ResponseBody
    public String checkUserIsHave(
            @RequestParam(required = false, value = "mobile") String mobile) {
        String isHave = "true";
        WelEmployee welEmployee =  new WelEmployee();
        if (StringUtils.isNotEmpty(mobile)) {
            welEmployee.setMobile(mobile);
        }
        WelEmployee employee = employeeService.findWelEmployee(welEmployee);
        if (employee == null) {
            isHave = "false";
        }
        return isHave;
    }


    /**
     * 获取手机验证码
     */
    @RequestMapping(value = "/getMobileCode", method = RequestMethod.POST)
    @ResponseBody
    public Map getMobileCode(
            @RequestParam(required =true, value = "account") String account) {
        Map map = Maps.newHashMap();
        try {
            Subject subject = SecurityUtils.getSubject();
            Session session = subject.getSession();
            session.setAttribute("recordTime", System.currentTimeMillis());//设置当前时间点
            log.debug("当前时间戳==" + System.currentTimeMillis());
            Integer valid = NumberUtils.getRandomInt(99999);
            System.out.println(valid);
            session.setAttribute("mobile", account);
            session.setAttribute("validCode", valid);//验证码
            SendMessageUtil sms = new SendMessageUtil();//发送短信
            String str = sms.sendSms(account, " 验证码为 "+valid+",请在页面中输入以完成验证。");
            map.put("success", "true");
            map.put("message", "一分钟后可重新获取");
        } catch (InvalidSessionException e) {
            log.error("获取验证码失败", e);
            map.put("success", "false");
            map.put("message", "获取验证码失败!");
            return map;
        }
        return map;
    }


    /**
     * 校验输入的短信验证码是否正确
     */
    @RequestMapping(value = "/checkMobileCode", method = RequestMethod.POST)
    @ResponseBody
    public  Map checkMobileCode(
            @RequestParam(required =true, value = "bound") String bound) {
        bound = bound.trim();
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        Map map = Maps.newHashMap();
        Object code = session.getAttribute("validCode");//发送的验证码
        Long recordTime = (Long) session.getAttribute("recordTime");//发送验证码的时候设置的时间点
        if (bound == "") {
            map.put("success", "false");
            map.put("message", "验证码不能为空");
            return map;
        } else if (code == null || recordTime == null){
            map.put("success", "false");
            map.put("message", "未获取验证码");
            return map;
        }
        Long now = System.currentTimeMillis();
        log.debug("1时间==" + (now));
        log.debug("2时间==" + (recordTime));
        log.debug("记录时间==" + (now - recordTime));
        if (!bound.equals(code.toString())) {
            map.put("success", "false");
            map.put("message", "验证码错误");
        } else if ((now - recordTime) > 65000) {
            map.put("success", "false");
            map.put("message", "验证码已经失效");
        } else {
            map.put("success", "true");
        }
        return map;
    }
    /**
     * 验证密码是否正确
     */
    @RequestMapping("/checkPassword")
    @ResponseBody
    public Map checkUserIsHave(
            @RequestParam(required = false, value = "password") String password,
            @RequestParam(required = false, value = "employeeId") String employeeId
            ) {
        Map map = Maps.newHashMap();
        map.put("success","true");
        WelEmployee welEmployee=employeeService.findWelEmployeeByEmployeeId(employeeId);
        String passwordOld=welEmployee.getPassword();
        boolean res=  Digests.validatePassword(password,passwordOld);
        if(!res){
            map.put("success","false");
        }
        return map;
    }



    public static void main(String[] args) {
        String stream = ValidateController.class.getClassLoader().getResource("static/image/validate/").getFile();
        File validateBaseFile = new File(stream);
        File vf = null;
        if (null != validateBaseFile) {
            File[] fs = validateBaseFile.listFiles();
            Random ra = new Random();
            if (null != fs && fs.length > 0) {
                int random = ra.nextInt(fs.length);
                vf = fs[random];
            }
        }
        System.out.println(vf.getName());
    }
}
  1. freemarker前端实现
<#macro imgValidate>
<#---->

向右滑动滑块填充拼图
刷新
<#--
--> <#--
-->

参考

原理:https://blog.csdn.net/qq_35992956/article/details/80801962
抠图:https://blog.csdn.net/ONROAD0612/article/details/81197158


希望对您有帮助,更多分享,欢迎关注本人公众号:猿来在痴。↓↓↓
微信扫码:
Java滑块验证码原理和实现_第2张图片

你可能感兴趣的:(Java)