文章目录
- 滑块验证码引入
- 滑块验证码原理
- 滑块验证码实现
- 参考
当前互联网流行使用滑块验证码,如下图是网易严选的登录验证部分。
很多网站使用滑块验证码提高网站安全性,为了做到真正的验证,必须要走后台服务器。
下面是java实现滑块验证的核心步骤:
- 从服务器随机取一张图片,并对图片上的随机x,y坐标和宽高一块区域抠图;
- 根据步骤一的坐标和宽高,使用二维数组保存原图上抠图区域的像素点坐标;
- 根据步骤二的坐标点,对原图的抠图区域的颜色进行处理。
- 完成以上步骤之后得到两张图(扣下来的方块图,带有抠图区域阴影的原图),将这两张图和抠图区域的y坐标传到前台,前端在移动方块验证时,将移动后的x坐标传递到后台与原来的x坐标作比较,如果在阈值内则验证通过。
- 请求验证的步骤:前台向后台发起请求,后台随机一张图片做处理将处理完的两张图片的base64,抠图y坐标和token(token为后台缓存验证码的唯一token,可以用缓存和分布式缓存)返回给前台。
- 前台滑动图片将x坐标和token作为参数请求后台验证,服务器根据token取出x坐标与参数的x进行比较。
- 说明
实现是基于springmvc的实现,前端为freemarker模板。 - 工具类
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<String,List<byte[]>> cache = CacheBuilder.newBuilder()
.maximumSize(2)
.expireAfterWrite(365, TimeUnit.DAYS)
.build(createCacheLoader());
private static CacheLoader<String, List<byte[]>> createCacheLoader() {
return new CacheLoader<String, List<byte[]>>(){
@Override
public List<byte[]> load(String key) throws Exception {
return null;
}
};
}
public static void set(String key,List<byte[]> value){
cache.put(key,value);
}
public static List<byte[]> get(String key){
List<byte[]> 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<byte[]> 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<byte[]> 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<byte[]> 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<String, Object> pictureTemplatesCut(byte[] templateFile, byte[] targetFile, String templateType, String targetType) throws Exception {
Map<String, Object> 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<String, Object> pictureTemplatesCut(File templateFile, File targetFile, String templateType, String targetType) throws Exception {
Map<String, Object> 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<ImageReader> 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));
}
}
- 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<String> imgList = JedisUtils.getList(JedisConfig.KEY_VALIDATE_IMG);
// List<String> tpllist = JedisUtils.getList(JedisConfig.KEY_VALIDATE_TPL);
// if (null == imgList || imgList.size() < 1 || tpllist == null || tpllist.size() < 1) {
// imgList = new ArrayList<String>();
// tpllist = new ArrayList<String>();
// 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<byte[]> imgList = ValidateCache.get(JedisConfig.KEY_VALIDATE_IMG);
List<byte[]> tpllist = ValidateCache.get(JedisConfig.KEY_VALIDATE_TPL);
if (null == imgList || imgList.size() < 1 || tpllist == null || tpllist.size() < 1) {
imgList = new ArrayList<byte[]>();
tpllist = new ArrayList<byte[]>();
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<String, Object> 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<String, Object> tokenObj = new HashMap<String, Object>();
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<byte[]> imgList, List<byte[]> 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<JarEntry> 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<String, Object> 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<String, String> getMobileCode(
@RequestParam(required =true, value = "account") String account) {
Map<String, String> 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<String, String> checkMobileCode(
@RequestParam(required =true, value = "bound") String bound) {
bound = bound.trim();
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
Map<String, String> 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<String, String> checkUserIsHave(
@RequestParam(required = false, value = "password") String password,
@RequestParam(required = false, value = "employeeId") String employeeId
) {
Map<String, String> 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());
}
}
- freemarker前端实现
<#macro imgValidate>
<#--<script src="${base}/js/jquery-3.1.1.min.js"></script>-->
<style>
.rightValidate {
width: 350px;
margin: 0px auto;
position: relative;
line-height: 33px;
height: 33px;
text-align: center;
z-index: 99;
}
.v_rightBtn {
position: absolute;
left: 0;
top: 0;
height: 33px;
width: 40px;
background: #ddd;
cursor: pointer;
}
.imgBtn{
width:50px;
height: 50px;
position: absolute;
left: 0;
display: none;
}
.imgBtn img{
width:100%
}
.imgBg{
position: relative;
width: 350px;
height: 0;
box-shadow: 0px 4px 8px #3C5476;
}
.hkinnerWrap{
border: 1px solid #eee;
}
.green{
border-color:#34C6C2 !important;
}
.green .v_rightBtn{
background: #34C6C2;
color: #fff;
}
.red{
border-color:red !important;
}
.red .v_rightBtn{
background: red;
color: #fff;
}
.refresh{
position: absolute;
width: 30px;
height: 30px;
right: 0;
top: 0;
font-size: 12px;
color: #fff;
text-shadow: 0px 0px 9px #333;
cursor: pointer;
display: none;
}
.notSel{
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-touch-callout: none;
}
</style>
<div class="comImageValidate rightValidate">
<div class="hkinnerWrap" style="height: 33px;position: relative">
<span class="v_rightBtn "><em class="notSel">→</em></span>
<span class="huakuai" style="font-size: 12px;line-height: 33px;color: #A9A9A9;">向右滑动滑块填充拼图</span>
<input type = "hidden" name="validX"/>
</div>
<div class="imgBg">
<div class="imgBtn">
<img />
</div>
<span class="refresh">刷新</span>
</div>
</div>
<#--<div>
<img src="data:image/png;base64,${newImage}"/>
<img src="data:image/jpg;base64,${sourceImage}" />
</div>-->
<script>
var tokenId = "";
var y = "";
var x = "";
$(".comImageValidate").ready(function () {
validateImageInit();
$(".refresh").click(function () {
validateImageInit();
})
$(".hkinnerWrap").mouseover(function(){
$(".imgBg").stop(false).animate({"height":"213px"},100,function () {
$(".imgBtn").css("display","block");
$(".refresh").css("display","block");
});
}).mouseleave(function () {
$(".imgBg").stop(false).animate({"height":"0"},100,function () {
$(".imgBtn").css("display","none");
$(".refresh").css("display","none");
});
});
$(".imgBg").mouseover(function () {
$(".imgBg").stop(false).animate({"height":"213px"},100,function () {
$(".imgBtn").css("display","block");
$(".refresh").css("display","block");
});
}).mouseleave(function () {
$(".imgBg").stop(false).animate({"height":"0"},100,function () {
$(".imgBtn").css("display","none");
$(".refresh").css("display","none");
});
})
$('.v_rightBtn').on({
mousedown: function(e) {
$(".huakuai").html("");
$(".hkinnerWrap").removeClass("red green")
var el = $(this);
var os = el.offset();
dx = e.pageX - os.left;
//$(document)
$(this).parents(".hkinnerWrap").off('mousemove');
$(this).parents(".hkinnerWrap").on('mousemove', function(e) {
var newLeft=e.pageX - dx;
el.offset({
left: newLeft
});
var newL=parseInt($(".v_rightBtn").css("left"));
if(newL<=0){
newL=0;
}else if (newL>=298){
newL=306;
}
$(".v_rightBtn").css("left",newL+"px");
$(".imgBtn").offset({
left: newLeft
});
$(".imgBtn").css("left",newL+"px")
}).on('mouseup', function(e) {
//$(document)
$(this).off('mousemove');
})
}
}).on("mouseup",function () {
$(this).parents(".hkinnerWrap").off('mousemove');
var l=$(this).css("left");
if(l.indexOf("px")!=-1){
l=l.substring(0,l.length-2);
}
x = l;
submitDate(l,y,tokenId)
})
});
/*图形验证*/
function submitDate(x,y,tokenId) {
$.ajax({
url:"${base}/validate/check?X="+x+"&Y="+y+"&token="+tokenId,
dataType:'json',
type: "POST",
success:function (data) {
if(data==true){
$(".hkinnerWrap").addClass("green").removeClass("red");
$(".hkinnerWrap input[name='validX']").val(x);
} else {
$(".hkinnerWrap").addClass("red").removeClass("green");
// setTimeout(function(){
// $(".hkinnerWrap").removeClass("red green");
// $(".v_rightBtn").css("left",0);
// $(".imgBtn").css("left",0);
// },500)
//validateImageInit();
}
}
})
}
/*初始化图形验证码*/
function validateImageInit() {
$.ajax({
url:"${base}/validate/init",
dataType:'json',
cache:false,
type: "get",
success:function (data) {
$(".huakuai").html("向右滑动滑块填充拼图");
$(".imgBg").css("background",'#fff url("data:image/jpg;base64,'+data.sourceImage+'")');
$(".imgBtn").css('top',data.Y+'px');
$(".imgBtn").find("img").attr("src","data:image/png;base64,"+data.newImage)
tokenId=data.token;
y=data.Y;
$(".hkinnerWrap").removeClass("red green");
$(".v_rightBtn").css("left",0);
$(".imgBtn").css("left",0);
},error:function(err){
console.log(err)
}
})
}
/**
* 再次进行用户拖动行为验证
* @returns {boolean}
*/
function reValidateDeal(){
var result = false;
$.ajax({
url:"${base}/validate/check?X="+x+"&Y="+y+"&token="+tokenId,
dataType:'json',
cache : false,
async : false,
type: "POST",
success:function (data) {
if(data==true){
result = true;
}else {
result = false;
}
}
})
return result;
}
</script>
<#--<div>
<img src="data:image/png;base64,${newImage}"/>
<img src="data:image/jpg;base64,${sourceImage}" />
</div>-->
</#macro>
原理:https://blog.csdn.net/qq_35992956/article/details/80801962
抠图:https://blog.csdn.net/ONROAD0612/article/details/81197158
臭味相投的朋友们,我在这里:
猿in小站:http://www.yuanin.net
csdn博客:https://blog.csdn.net/jiabeis
简书:https://www.jianshu.com/u/4cb7d664ec4b
微信免费订阅号“猿in”
注意:本文归作者所有,未经作者允许,不得转载