1. 程式人生 > 程式設計 >js+java實現登入滑動圖片驗證

js+java實現登入滑動圖片驗證

最新需要公司要求在不改變原來的登入邏輯的情況下,將原來的驗證碼登入的形式改成滑動圖片的形式!下面是做出來的效果:

js+java實現登入滑動圖片驗證

實現思路:所有的圖片資料,驗證全部由後端來做。前端呼叫介面,後端會返回兩張經過base64加密的圖片資訊,分別是背景圖片和滑塊圖片,前端滑動滑塊以後將X方向的滑動距離傳回後端做驗證,驗證成功以後再做後續的登入邏輯驗證,以下是完整的過程:

獲取背景圖,我這邊是在FTP上放了10張圖片,隨機獲取一張。

@LogAnnotation(description = "web獲取滑動圖片資訊")
 @ApiOperation(value = "web獲取滑動圖片資訊",httpMethod = "POST",response = Result.class,notes = "")
 @RequiresPermissions(value = "code/picture/msg")
 @RequestMapping(value = "code/picture/msg",method = RequestMethod.POST)
 @ResponseBody
 public Response getPictureCode(HttpServletRequest request) {
 Map<String,Object> pictureMap = new HashMap<>();
 try {
 //隨機獲取需要切成的圖片形狀
 Integer templateNum = new Random().nextInt(10) + 1;
 String randomStr = String.valueOf(templateNum);
 if(templateNum < 10){
 randomStr = "0"+randomStr;
 }
 InputStream tempInputStream = FileUtils.downloadFtpFile(Constant.POCTURE_CHECK_PATH,randomStr+".jpg");
 //根據源圖片和摳圖形狀生成新的圖片資訊以流的形式返回到前端
 pictureMap = VerifyImageUtil.getVerifyImage(tempInputStream);
 //將剪裁好了以後的圖片資訊以當前時間戳為key,存入redis(這一步是為了對圖片資訊做過期處理,根據自己需求做)
 String tempTime = String.valueOf(System.currentTimeMillis());
 redisUtils.hset(Constant.REDIS_LOGIN_PICTURE_CODE,tempTime,pictureMap.get("locationX").toString(),60);
 pictureMap.put("tempTime",tempTime);
 //移出隨機生成的摳圖位置座標,不返回給前端
 pictureMap.remove("locationX");
 return new ObjectResponse<Map<String,Object>>(pictureMap);
 }catch(Exception e){
 logger.error("code/picture/msg",e);
 return new FailedResponse();
 }
 }

FTP下載方法程式碼

public static FTPClient ftpLogin(){
 String ip = p.getProperty("ftp的ip");
 String username = p.getProperty("ftp的user");
 String password = p.getProperty("ftp的pwd");
 int ftpPort = Integer.parseInt(p.getProperty("ftp的port"));
  FTPClient ftpClient = new FTPClient();
 try { 
   ftpClient.connect(ip,ftpPort );// 連線FTP伺服器 
   ftpClient.login(username,password );// 登陸FTP伺服器 
   if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { 
    ftpClient.disconnect(); 
   }
  } catch (SocketException e) { 
   e.printStackTrace(); 
  } catch (IOException e) { 
   e.printStackTrace(); 
  } 
 return ftpClient;
 }
 
public static InputStream downloadFtpFile(String ftpPath,String fileName) { 
  FTPClient ftpClient = ftpLogin();
  try {
   ftpClient.setControlEncoding("UTF-8"); // 中文支援 
   ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); 
   ftpClient.enterLocalPassiveMode(); 
   ftpClient.changeWorkingDirectory(ftpPath); 
   InputStream inputStream = ftpClient.retrieveFileStream(new String(fileName.getBytes("UTF-8"),"ISO-8859-1"));
   //ftpClient.completePendingCommand();
   return inputStream;
  } catch (Exception e) { 
   e.printStackTrace(); 
  }finally {
   try {
 ftpClient.logout();
 } catch (IOException e) {
 e.printStackTrace();
 } 
 }
 return null;
 } 

摳圖工具類VerifyImageUtil

public class VerifyImageUtil {
 
 /**
 * 原始檔寬度
 */
 private static int ORI_WIDTH = 296;
 /**
 * 原始檔高度
 */
 private static int ORI_HEIGHT = 182;
 /**
 * 模板圖寬度
 */
 private static int CUT_WIDTH = 50;
 /**
 * 模板圖高度
 */
 private static int CUT_HEIGHT = 50;
 /**
 * 摳圖凸起圓心
 */
 private static int circleR = 6;
 /**
 * 摳圖內部矩形填充大小
 */
 private static int RECTANGLE_PADDING = 6;
 /**
 * 摳圖的邊框寬度
 */
 private static int SLIDER_IMG_OUT_PADDING = 1;
 
 
 /**
 * 根據傳入的路徑生成指定驗證碼圖片
 *
 * @param filePath
 * @return
 * @throws IOException
 */
 public static Map<String,Object> getVerifyImage(InputStream filePath) throws IOException {
 BufferedImage srcImage = ImageIO.read(filePath);
 int locationX = CUT_WIDTH + new Random().nextInt(srcImage.getWidth() - CUT_WIDTH * 2);
 int locationY = CUT_HEIGHT + new Random().nextInt(srcImage.getHeight() - CUT_HEIGHT) / 2;
 BufferedImage markImage = new BufferedImage(CUT_WIDTH,CUT_HEIGHT,BufferedImage.TYPE_4BYTE_ABGR);
 int[][] data = getBlockData();
 cutImgByTemplate(srcImage,markImage,data,locationX,locationY);
 Map<String,Object> resultMap = new HashMap<>();
  //放入背景圖的加密資訊
 resultMap.put("srcImage",getImageBASE64(srcImage));
  //放入滑塊圖的加密資訊
 resultMap.put("markImage",getImageBASE64(markImage));
  //放入摳圖位置的X方向的資訊,用於驗證滑塊位置是否正確
 resultMap.put("locationX",locationX);
  //放入摳圖位置的Y方向的資訊,用於前端控制定位資訊
 resultMap.put("locationY",locationY);
 return resultMap;
 }
 
 
 /**
 * 生成隨機滑塊形狀
 * <p>
 * 0 透明畫素
 * 1 滑塊畫素
 * 2 陰影畫素
 * @return int[][]
 */
 private static int[][] getBlockData() {
 int[][] data = new int[CUT_WIDTH][CUT_HEIGHT];
 Random random = new Random();
 //(x-a)²+(y-b)²=r²
 //x中心位置左右5畫素隨機
 double x1 = RECTANGLE_PADDING + (CUT_WIDTH - 2 * RECTANGLE_PADDING) / 2.0 - 5 + random.nextInt(10);
 //y 矩形上邊界半徑-1畫素移動
 double y1_top = RECTANGLE_PADDING - random.nextInt(3);
 double y1_bottom = CUT_HEIGHT - RECTANGLE_PADDING + random.nextInt(3);
 double y1 = random.nextInt(2) == 1 ? y1_top : y1_bottom;
 
 
 double x2_right = CUT_WIDTH - RECTANGLE_PADDING - circleR + random.nextInt(2 * circleR - 4);
 double x2_left = RECTANGLE_PADDING + circleR - 2 - random.nextInt(2 * circleR - 4);
 double x2 = random.nextInt(2) == 1 ? x2_right : x2_left;
 double y2 = RECTANGLE_PADDING + (CUT_HEIGHT - 2 * RECTANGLE_PADDING) / 2.0 - 4 + random.nextInt(10);
 
 double po = Math.pow(circleR,2);
 for (int i = 0; i < CUT_WIDTH; i++) {
 for (int j = 0; j < CUT_HEIGHT; j++) {
 //矩形區域
 boolean fill;
 if ((i >= RECTANGLE_PADDING && i < CUT_WIDTH - RECTANGLE_PADDING)
  && (j >= RECTANGLE_PADDING && j < CUT_HEIGHT - RECTANGLE_PADDING)) {
  data[i][j] = 1;
  fill = true;
 } else {
  data[i][j] = 0;
  fill = false;
 }
 //凸出區域
 double d3 = Math.pow(i - x1,2) + Math.pow(j - y1,2);
 if (d3 < po) {
  data[i][j] = 1;
 } else {
  if (!fill) {
  data[i][j] = 0;
  }
 }
 //凹進區域
 double d4 = Math.pow(i - x2,2) + Math.pow(j - y2,2);
 if (d4 < po) {
  data[i][j] = 0;
 }
 }
 }
 //邊界陰影
 for (int i = 0; i < CUT_WIDTH; i++) {
 for (int j = 0; j < CUT_HEIGHT; j++) {
 //四個正方形邊角處理
 for (int k = 1; k <= SLIDER_IMG_OUT_PADDING; k++) {
  //左上、右上
  if (i >= RECTANGLE_PADDING - k && i < RECTANGLE_PADDING
  && ((j >= RECTANGLE_PADDING - k && j < RECTANGLE_PADDING)
  || (j >= CUT_HEIGHT - RECTANGLE_PADDING - k && j < CUT_HEIGHT - RECTANGLE_PADDING +1))) {
  data[i][j] = 2;
  }
 
  //左下、右下
  if (i >= CUT_WIDTH - RECTANGLE_PADDING + k - 1 && i < CUT_WIDTH - RECTANGLE_PADDING + 1) {
  for (int n = 1; n <= SLIDER_IMG_OUT_PADDING; n++) {
  if (((j >= RECTANGLE_PADDING - n && j < RECTANGLE_PADDING)
   || (j >= CUT_HEIGHT - RECTANGLE_PADDING - n && j <= CUT_HEIGHT - RECTANGLE_PADDING ))) {
  data[i][j] = 2;
  }
  }
  }
 }
 
 if (data[i][j] == 1 && j - SLIDER_IMG_OUT_PADDING > 0 && data[i][j - SLIDER_IMG_OUT_PADDING] == 0) {
  data[i][j - SLIDER_IMG_OUT_PADDING] = 2;
 }
 if (data[i][j] == 1 && j + SLIDER_IMG_OUT_PADDING > 0 && j + SLIDER_IMG_OUT_PADDING < CUT_HEIGHT && data[i][j + SLIDER_IMG_OUT_PADDING] == 0) {
  data[i][j + SLIDER_IMG_OUT_PADDING] = 2;
 }
 if (data[i][j] == 1 && i - SLIDER_IMG_OUT_PADDING > 0 && data[i - SLIDER_IMG_OUT_PADDING][j] == 0) {
  data[i - SLIDER_IMG_OUT_PADDING][j] = 2;
 }
 if (data[i][j] == 1 && i + SLIDER_IMG_OUT_PADDING > 0 && i + SLIDER_IMG_OUT_PADDING < CUT_WIDTH && data[i + SLIDER_IMG_OUT_PADDING][j] == 0) {
  data[i + SLIDER_IMG_OUT_PADDING][j] = 2;
 }
 }
 }
 return data;
 }
 
 /**
 * 裁剪區塊
 * 根據生成的滑塊形狀,對原圖和裁剪塊進行變色處理
 * @param oriImage 原圖
 * @param targetImage 裁剪圖
 * @param blockImage 滑塊
 * @param x   裁剪點x
 * @param y   裁剪點y
 */
 private static void cutImgByTemplate(BufferedImage oriImage,BufferedImage targetImage,int[][] blockImage,int x,int y) {
 for (int i = 0; i < CUT_WIDTH; i++) {
 for (int j = 0; j < CUT_HEIGHT; j++) {
 int _x = x + i;
 int _y = y + j;
 int rgbFlg = blockImage[i][j];
 int rgb_ori = oriImage.getRGB(_x,_y);
 // 原圖中對應位置變色處理
 if (rgbFlg == 1) {
  //摳圖上覆制對應顏色值
  targetImage.setRGB(i,j,rgb_ori);
  //原圖對應位置顏色變化
  oriImage.setRGB(_x,_y,rgb_ori & 0x363636);
 } else if (rgbFlg == 2) {
  targetImage.setRGB(i,Color.WHITE.getRGB());
  oriImage.setRGB(_x,Color.GRAY.getRGB());
 }else if(rgbFlg == 0){
  //int alpha = 0;
  targetImage.setRGB(i,rgb_ori & 0x00ffffff);
 }
 }
 
 }
 }
 
 
 /**
 * 隨機獲取一張圖片物件
 * @param path
 * @return
 * @throws IOException
 */
 public static BufferedImage getRandomImage(String path) throws IOException {
 File files = new File(path);
 File[] fileList = files.listFiles();
 List<String> fileNameList = new ArrayList<>();
 if (fileList!=null && fileList.length!=0){
 for (File tempFile:fileList){
 if (tempFile.isFile() && tempFile.getName().endsWith(".jpg")){
  fileNameList.add(tempFile.getAbsolutePath().trim());
 }
 }
 }
 Random random = new Random();
 File imageFile = new File(fileNameList.get(random.nextInt(fileNameList.size())));
 return ImageIO.read(imageFile);
 }
 
 /**
 * 將IMG輸出為檔案
 * @param image
 * @param file
 * @throws Exception
 */
 public static void writeImg(BufferedImage image,String file) throws Exception {
 byte[] imagedata = null;
 ByteArrayOutputStream bao=new ByteArrayOutputStream();
 ImageIO.write(image,"png",bao);
 imagedata = bao.toByteArray();
 FileOutputStream out = new FileOutputStream(new File(file));
 out.write(imagedata);
 out.close();
 }
 
 /**
 * 將圖片轉換為BASE64
 * @param image
 * @return
 * @throws IOException
 */
 public static String getImageBASE64(BufferedImage image) throws IOException {
 ByteArrayOutputStream out = new ByteArrayOutputStream();
 ImageIO.write(image,out);
 //轉成byte陣列
 byte[] bytes = out.toByteArray();
 BASE64Encoder encoder = new BASE64Encoder();
 //生成BASE64編碼
 return encoder.encode(bytes);
 }
 
 /**
 * 將BASE64字串轉換為圖片
 * @param base64String
 * @return
 */
 public static BufferedImage base64StringToImage(String base64String) {
 try {
 BASE64Decoder decoder=new BASE64Decoder();
 byte[] bytes1 = decoder.decodeBuffer(base64String);
 ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
 return ImageIO.read(bais);
 } catch (IOException e) {
 e.printStackTrace();
 }
 return null;
 }
}

前端收到請求成功返回資訊以後,對資料做解析

js+java實現登入滑動圖片驗證

// 設定圖片的src屬性
 $("#背景圖的ID").attr("src","data:image/png;base64," + result.datas.srcImage);
 $("#滑塊圖的ID").attr("src"," + result.datas.markImage);
 $("#滑塊圖的ID").css("top",result.datas.locationY);
 /* 初始化按鈕拖動事件 */
 // 滑鼠點選事件
 $("#滑塊拖動條").mousedown(function(e) {
  e = e || window.event;
  // 滑鼠在滑塊按下切換滑塊背景
  var left = e.pageX;//記錄滑鼠按下時的座標 X軸值
  var num = 0;
  // 滑鼠移動事件
  document.onmousemove = function(e) {
  var nowLeft = e.pageX;
  num = nowLeft-left;
  if(num <= 0){
  num = 0;
  }else if(num >= 251){
  num= 251;
  }
  $("#滑塊拖動條").css("width",(42+num) + "px");
  $("#滑塊拖動條圖片").css("left",(num) + "px");
  $("#滑塊拖動條").css("background-color","#3c55ff");
  };
  // 滑鼠鬆開事件
  document.onmouseup = function(e) {
  left = 0;
  document.onmousemove = null;
  document.onmouseup = null;
      //鬆開以後進行後續的驗證......
  
  };
 });

以上就是完整的過程了.

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。