时间:2017年07月09日星期日
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学源码:无
学习源码:https://github.com/zccodere/s...
主要内容
验证码历史 课程内容 不同方案对比 设计与实现 总结1-2 验证码历史
验证码历史
无验证码:垃圾骚扰 Luis von Ahn:Captcha 不断的升级 去验证码
常见验证码
1-3 项目介绍完成类似最后一张图片的验证码设计与实现
对比方案 完成设计 编码实现 结果演示
结果演示
不同方案对比(一)
浏览器请求验证码图片 服务器返回验证码图片及图片标识 浏览器提交验证码 服务器验证图片内容及标识
不同方案对比(二)
浏览器请求验证码图片
服务器返回验证码图片及图片标识
浏览器提交验证码
图片文字/计算结果等
坐标
服务器验证
验证图片内容及标识
验证坐标及标识
设计与实现
包结构 --controller、generator 主要类及作用 --Image:生成验证码图片核心类 --BufferedImageWrap:图片包装类 --ImageGroup:原始图片分组 --GenerateImageGroup:单次验证使用图片组 --Cache:单次验证数据缓存 --LoginController
程序设计:技术选择
教学使用
SpringMVC JSP Spring(4.0.5)
学习使用
SpringBoot Freemarker
思路整理
每次显示几张图片:由8张小图组成的一张大图 答案图片位置 选中位置坐标 坐标验证 前后关联第二章:图片生成及页面显示 2-1 页面结构及LoginController介绍
部分代码演示:源码请到我的github地址查看
login.html
登录
LoginController类
package com.myimooc.identifying.controller;
import java.io.IOException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.myimooc.identifying.generator.Image;
import com.myimooc.identifying.generator.ImageResult;
/**
* 登录控制器
* @author ZhangCheng on 2017-07-09
*
*/
@Controller
public class LoginController {
/**
* 登录主页
* @param model
* @param request
* @param response
* @return
*/
@RequestMapping("/login")
public String identify(Model model,HttpServletRequest request,HttpServletResponse response){
try{
ImageResult imageResult = Image.generateImage();
model.addAttribute("file", imageResult.getName());
model.addAttribute("tip", imageResult.getTip());
System.out.println(imageResult.getName() + imageResult.getTip());
Cookie cookie = new Cookie("note",imageResult.getUniqueKey());
response.addCookie(cookie);
request.getSession().setAttribute(imageResult.getUniqueKey(), imageResult);
}catch(Exception e){
System.out.println("获取图片失败");
e.printStackTrace();
}
return "login";
}
/**
* 刷新图片
*
* @param request
* @return
* @throws IOException
*/
@RequestMapping(value = "/getPng")
@ResponseBody
public String getPng(HttpServletRequest request) throws IOException{
ImageResult imageResult = Image.generateImage();
((HttpServletRequest) request).getSession().setAttribute("imageResult", imageResult);
return imageResult.getName() + "," + imageResult.getTip();
}
/**
* 验证消息
*
* @param location
* @param request
* @param userName
* @param password
* @return
*/
@PostMapping("/dologin")
@ResponseBody
public String doLogin(String location, HttpServletRequest request, String userName, String password, RedirectAttributes redirectAttributes) {
System.out.println("验证坐标:"+ location);
Cookie[] cookies = ((HttpServletRequest) request).getCookies();
Cookie note = null;
for (Cookie cookie : cookies) {
if (cookie.getName().equals("note")) {
note = cookie;
break;
}
}
if(null == note){
return "ERROR";
}
ImageResult imageResult = (ImageResult)request.getSession().getAttribute(note.getValue());
if(validate(location,imageResult)){
return "OK";
}
return "ERROR";
}
/**
* 验证是否正确
* @param locationString
* @param imageResult
* @return
*/
private boolean validate(String locationString, ImageResult imageResult) {
String[] resultArray = locationString.split(";");
int[][] array = new int[resultArray.length][2];
for (int i = 0; i=0 && y<75){
return xLocation(x);
}else if(y >=75 && y<=150){
return xLocation(x)+4;
}else{
// 脏数据
return -1;
}
}
private int xLocation(int x) {
if(x >=0 && x<75){
return 0;
}else if(x >=75 && x<150){
return 1;
}else if(x >=150 && x<225){
return 2;
}else if(x >=225 && x<=300){
return 3;
}else{
// 脏数据
return -1;
}
}
}
2-2 如何生成图片generateImage
Image类
package com.myimooc.identifying.generator;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import javax.imageio.ImageIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 生成验证码图片核心类
* @author ZhangCheng on 2017-07-09
*
*/
public class Image {
private static final Logger log= LoggerFactory.getLogger(Image.class);
private static Map imageGroupMap=new HashMap<>();
private static Map> countGroupMap=new HashMap<>();
/**
* 功能:由小图生成一种大图
* @return
* @throws IOException
*/
public static ImageResult generateImage()throws IOException{
// 初始化
initImageGroup();
log.debug("初始化完成");
GenerateImageGroup generateImageGroup = randomImageGroups();
List images = new ArrayList();
// 找到图片干扰项
for (ImageGroup group : generateImageGroup.getGroups()) {
for (String imgName : group.getImages()) {
images.add(new BufferedImageWrap(false,getBufferedImage(imgName)));
}
}
// 找到图片答案项
for(String imgName : generateImageGroup.getKeyGroup().getImages()){
images.add(new BufferedImageWrap(true,getBufferedImage(imgName)));
}
return mergeImage(images,generateImageGroup.getKeyGroup().getName());
}
/**
* 功能:根据图片名称获得图片缓冲流
* @param imgName
* @return
* @throws IOException
*/
private static BufferedImage getBufferedImage(String imgName)throws IOException {
String rootPath = Image.class.getClassLoader().getResource("sourceImage/").getPath();
String imgPath = rootPath + imgName;
File file = new File(imgPath);
return ImageIO.read(file);
}
/**
* 功能:将小图合并成一种大图
* @param images
* @param name
* @return
*/
private static ImageResult mergeImage(List imageWraps, String tip) {
Collections.shuffle(imageWraps);
// 原始图片宽200像素,高200像素
int width = 200;
int high = 200;
int totalWidth = width * 4;
BufferedImage destImage = new BufferedImage(totalWidth,400,BufferedImage.TYPE_INT_RGB);
int x1 = 0;
int x2 = 0;
int order = 0;
List keysOrderList = new ArrayList();
StringBuilder keysOrder = new StringBuilder();
Set keySet = new HashSet();
for(BufferedImageWrap image : imageWraps){
int[] rgb = image.getBufferedImage().getRGB(0, 0, width, high, null, 0, width);
if(image.isKey()){
keysOrderList.add(order);
int x = (order % 4) * 200;
int y = order < 4 ? 0:200;
keySet.add(order);
keysOrder.append(order).append("(").append(x).append(",").append(y).append(")|");
}
if(order < 4 ){
// 设置上半部分的RGB
destImage.setRGB(x1, 0, width,high,rgb,0,width);
x1 += width;
}else{
destImage.setRGB(x2, high, width,high,rgb,0,width);
x2 += width;
}
order++;
}
keysOrder.deleteCharAt(keysOrder.length() - 1);
System.out.println("答案位置:" + keysOrder);
String fileName = UUID.randomUUID().toString().replaceAll("-", "") + ".jpeg";
String rootPath = Image.class.getClassLoader().getResource("static/targetImage/").getPath();
//String rootPath = Image.class.getClassLoader().getResource("sourceImage/").getPath();
log.info("根路径:{}",rootPath);
String fileUrl = rootPath + fileName;
// 保存图片
saveImage(destImage,fileUrl,"png");
ImageResult ir = new ImageResult();
ir.setName(fileName);
ir.setKeySet(keySet);
ir.setUniqueKey(fileName);
ir.setTip(tip);
return ir;
}
/**
* 功能:将图片写入指定的路径
* @param destImage
* @param fileUrl
* @param string
*/
private static void saveImage(BufferedImage destImage, String fileUrl, String format) {
File file=new File(fileUrl);
log.debug(file.getAbsolutePath());
try {
ImageIO.write(destImage,format,file);
} catch (IOException e) {
log.info("图片写入失败");
e.printStackTrace();
}
}
/**
* 功能:随机生成图片答案和干扰组
* @return
*/
private static GenerateImageGroup randomImageGroups(){
List result = new ArrayList();
int num = random(0, imageGroupMap.size() - 1);
String name = new ArrayList(imageGroupMap.keySet()).get(num);
ImageGroup keyGroup = imageGroupMap.get(name);
Map> thisCountGroupMap = new HashMap<>(countGroupMap);
thisCountGroupMap.get(keyGroup.getCount()).remove(name);
// 假设总量8个,每种名称图片只有2个或4个,为了逻辑简单些
int leftCount = 8 - keyGroup.getCount();
if(leftCount == 4){
if(new Random().nextInt() % 2 == 0){
List groups = new ArrayList(thisCountGroupMap.get(4).values());
if(groups.size() > 1){
num = random(0, groups.size() - 1);
}else{
num = 0;
}
result.add(groups.get(num));
}else{
List groups = new ArrayList(thisCountGroupMap.get(2).values());
int num1 = random(0, groups.size() - 1);
result.add(groups.get(num1));
int num2 = random(0, groups.size() - 1,num1);
result.add(groups.get(num2));
}
}else if(leftCount == 6){
if(new Random().nextInt() % 2 == 0){
List groups1 = new ArrayList(thisCountGroupMap.get(4).values());
int num1 = random(0, groups1.size() - 1);
result.add(groups1.get(num1));
List groups2 = new ArrayList(thisCountGroupMap.get(2).values());
int num2 = random(0, groups2.size() - 1);
result.add(groups2.get(num2));
}else{
List groups = new ArrayList(thisCountGroupMap.get(2).values());
int num1 = random(0, groups.size() - 1);
result.add(groups.get(num1));
int num2 = random(0, groups.size() - 1,num1);
result.add(groups.get(num2));
int num3 = random(0, groups.size() - 1,num1,num2);
result.add(groups.get(num3));
}
}
return new GenerateImageGroup(keyGroup, result);
}
/**
* 功能:初始化图片组。后期优化可从数据库获取
*/
private static void initImageGroup(){
ImageGroup group1 = new ImageGroup("包包",4,"bao/1.jpg","bao/2.jpg","bao/3.jpg","bao/4.jpg");
ImageGroup group2 = new ImageGroup("老虎",4,"laohu/1.jpg","laohu/2.jpg","laohu/3.jpg","laohu/4.jpg");
ImageGroup group3 = new ImageGroup("糖葫芦",4,"tanghulu/1.jpg","tanghulu/2.jpg","tanghulu/3.jpg","tanghulu/4.jpg");
ImageGroup group4 = new ImageGroup("小慕",4,"xiaomu/1.jpg","xiaomu/2.jpg","xiaomu/3.jpg","xiaomu/4.jpg");
ImageGroup group5 = new ImageGroup("柚子",4,"youzi/1.jpg","youzi/2.jpg","youzi/3.jpg","youzi/4.jpg");
ImageGroup group6 = new ImageGroup("订书机",2,"dingshuji/1.jpg","dingshuji/2.jpg");
ImageGroup group7 = new ImageGroup("蘑菇",2,"mogu/1.jpg","mogu/2.jpg");
ImageGroup group8 = new ImageGroup("磁铁",2,"citie/1.jpg","citie/2.jpg");
ImageGroup group9 = new ImageGroup("土豆",4,"tudou/1.jpg","tudou/2.jpg","tudou/3.jpg","tudou/4.jpg");
ImageGroup group10 = new ImageGroup("兔子",4,"tuzi/1.jpg","tuzi/2.jpg","tuzi/3.jpg","tuzi/4.jpg");
ImageGroup group11 = new ImageGroup("仙人球",4,"xianrenqiu/1.jpg","xianrenqiu/2.jpg","xianrenqiu/3.jpg","xianrenqiu/4.jpg");
initMap(group1,group2,group3,group4,group5,group6,group7,group8,group9,group10,group11);
}
/**
* 功能:初始化所有图片组
* @param groups
*/
private static void initMap(ImageGroup... groups) {
for (ImageGroup group : groups) {
imageGroupMap.put(group.getName(),group);
if(!countGroupMap.containsKey(group.getCount())){
countGroupMap.put(group.getCount(),new HashMap());
}
countGroupMap.get(group.getCount()).put(group.getName(),group);
}
}
/**
* 功能:生成随机整数
* @param min
* @param max
* @return
*/
private static int random(int min,int max){
Random random = new Random();
return random.nextInt(max - min + 1) + min;
}
/**
* 功能:生成随机整数不在指定整数数组里
* @param min
* @param max
* @param not
* @return
*/
private static int random(int min,int max,Integer... not){
int num = random(min,max);
List notList = Arrays.asList(not);
while(notList.contains(num)){
num = random(min,max);
}
return num;
}
}
2-3 如何将图片融合mergeImage
/**
* 功能:将小图合并成一种大图
* @param images
* @param name
* @return
*/
private static ImageResult mergeImage(List imageWraps, String tip) {
Collections.shuffle(imageWraps);
// 原始图片宽200像素,高200像素
int width = 200;
int high = 200;
int totalWidth = width * 4;
BufferedImage destImage = new BufferedImage(totalWidth,400,BufferedImage.TYPE_INT_RGB);
int x1 = 0;
int x2 = 0;
int order = 0;
List keysOrderList = new ArrayList();
StringBuilder keysOrder = new StringBuilder();
Set keySet = new HashSet();
for(BufferedImageWrap image : imageWraps){
int[] rgb = image.getBufferedImage().getRGB(0, 0, width, high, null, 0, width);
if(image.isKey()){
keysOrderList.add(order);
int x = (order % 4) * 200;
int y = order < 4 ? 0:200;
keySet.add(order);
keysOrder.append(order).append("(").append(x).append(",").append(y).append(")|");
}
if(order < 4 ){
// 设置上半部分的RGB
destImage.setRGB(x1, 0, width,high,rgb,0,width);
x1 += width;
}else{
destImage.setRGB(x2, high, width,high,rgb,0,width);
x2 += width;
}
order++;
}
keysOrder.deleteCharAt(keysOrder.length() - 1);
System.out.println("答案位置:" + keysOrder);
String fileName = UUID.randomUUID().toString().replaceAll("-", "") + ".jpeg";
String rootPath = Image.class.getClassLoader().getResource("static/targetImage/").getPath();
//String rootPath = Image.class.getClassLoader().getResource("sourceImage/").getPath();
log.info("根路径:{}",rootPath);
String fileUrl = rootPath + fileName;
// 保存图片
saveImage(destImage,fileUrl,"png");
ImageResult ir = new ImageResult();
ir.setName(fileName);
ir.setKeySet(keySet);
ir.setUniqueKey(fileName);
ir.setTip(tip);
return ir;
}
/**
* 功能:将图片写入指定的路径
* @param destImage
* @param fileUrl
* @param string
*/
private static void saveImage(BufferedImage destImage, String fileUrl, String format) {
File file=new File(fileUrl);
log.debug(file.getAbsolutePath());
try {
ImageIO.write(destImage,format,file);
} catch (IOException e) {
log.info("图片写入失败");
e.printStackTrace();
}
}
第三章:验证过程及总结
3-1 验证验证码过程及总结
总结
验证码历史 不同方案对比 设计与实现 总结
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/67325.html
摘要:时间年月日星期五说明本文部分内容均来自慕课网。线性堆叠式二维码示意图矩阵式二维码在一个矩形空间通过黑白像素在矩阵中的不同分布进行编码。 时间:2017年06月23日星期五说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:无个人学习源码:https://github.com/zccodere/s... 第一章:二维码的概念 1-1 二维码概述...
摘要:时间年月日星期一说明本文部分内容均来自慕课网。多用于网络加密。散列函数函数或消息摘要函数主要作用散列函数用来验证数据的完整性。 时间:2017年4月10日星期一说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:https://github.com/zccodere/s...个人学习源码:https://github.com/zccodere...
摘要:慕课网发送邮件学习总结时间年月日星期六说明本文部分内容均来自慕课网。 慕课网《Spring Boot 发送邮件》学习总结 时间:2018年09月08日星期六 说明:本文部分内容均来自慕课网。@慕课网:https://www.imooc.com 教学源码:https://github.com/ityouknow/... 学习源码:https://github.com/zccoder...
摘要:时间年月日星期五说明本文部分内容均来自慕课网。本套课程介绍微信公众号开发,主要涉及公众号介绍编辑模式介绍开发模式介绍等。慕课网是垂直的互联网技能免费学习网站。 时间:2017年08月11日星期五说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学源码:https://github.com/zccodere/s...学习源码:https://github...
摘要:时间年月日星期三说明本文部分内容均来自慕课网。用户过生日,系统发送生日祝福邮件。将最新活动和优惠以邮件的形式告知会员。通常把处理用户请求邮件发送请求的邮件服务器称为服务器。提供了加密的协议被称为。 时间:2017年06月07日星期三说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:无个人学习源码:https://github.com/zcc...
阅读 1487·2019-08-30 15:44
阅读 2310·2019-08-30 13:49
阅读 1876·2019-08-26 13:54
阅读 3763·2019-08-26 10:20
阅读 3677·2019-08-23 17:18
阅读 3490·2019-08-23 17:05
阅读 2327·2019-08-23 15:38
阅读 1244·2019-08-23 14:35