java
主页 > 软件编程 > java >

js+java实现登录滑动图片验证的代码

2020-03-18 | 秩名 | 点击:

最新需要公司要求在不改变原来的登录逻辑的情况下,将原来的验证码登录的形式改成滑动图片的形式!下面是做出来的效果:

实现思路:所有的图片数据,验证全部由后端来做。前端调用接口,后端会返回两张经过base64加密的图片信息,分别是背景图片和滑块图片,前端滑动滑块以后将X方向的滑动距离传回后端做验证,验证成功以后再做后续的登录逻辑验证,以下是完整的过程:

获取背景图,我这边是在FTP上放了10张图片,随机获取一张。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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下载方法代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
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, j, Color.WHITE.getRGB());
  oriImage.setRGB(_x, _y, Color.GRAY.getRGB());
 }else if(rgbFlg == 0){
  //int alpha = 0;
  targetImage.setRGB(i, j, 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,"png",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;
 }
}

前端收到请求成功返回信息以后,对数据做解析

-[-/a>

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 设置图片的src属性
 $("#背景图的ID").attr("src", "data:image/png;base64," + result.datas.srcImage);
 $("#滑块图的ID").attr("src", "data:image/png;base64," + 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;
      //松开以后进行后续的验证......
  
  };
 });

以上就是完整的过程了.

原文链接:https://blog.csdn.net/qq_38400856/article/details/104915350
相关文章
最新更新