最近发现一些网站的验证码全部换成了“极验”和“点触”的,发现QQ的注册也是与“点触”的相似。就想尝试实现一个。

先上效果图:

下面贴上主要思路及代码:

第一步:得到常用汉字列表

        public static List<string> GetChineseWordList()
{
string cacheKey = "VCode_ChineseWordList";
return cache.GetCacheByFuc_NoRemove<List<string>>(cacheKey, new CacheFunc(delegate()
{
var list = new List<byte>();
for (int area = ; area <= ; area++)
{
for (int place2 = (area == ) ? : , place = ; place <= place2; place++)
{
list.Add((byte)(area + 0xa0));
list.Add((byte)(place + 0xa0));
}
}
string words = Encoding.GetEncoding("GB2312").GetString(list.ToArray());
return words.Select(m => m.ToString()).ToList();
}));
}

第二步:获取随机背景图片列表

        public static List<string> GetBackGroundImageList(string imageDir)
{
string cacheKey = "VCode_BackGroundImageList";
return cache.GetCacheByFuc_NoRemove<List<string>>(cacheKey, new CacheFunc(delegate()
{
List<string> list = new List<string>(); DirectoryInfo theFolder = new DirectoryInfo(imageDir);
FileInfo[] fileInfo = theFolder.GetFiles();
foreach (FileInfo file in fileInfo)
{
list.Add(file.FullName);
}
return list;
}));
}

第三步:获取随机汉字列表

        public static List<string> GetRandChineseWordList(List<string> chineseWordList, int length)
{
List<string> list = new List<string>();
for (var i = ; i < length; i++)
{
Random rnd = new Random((i + ) * unchecked((int)DateTime.Now.Ticks));
int index = rnd.Next(, chineseWordList.Count());
list.Add(chineseWordList[index]);
}
return list;
}

第四步:生成汉字水印并返回需要验证的字符及位置信息

        public static List<WordPos> DrawWord(Graphics picture, Bitmap bmp, List<string> randWordList, Random rnd)
{
picture.CompositingQuality = CompositingQuality.HighQuality;
picture.SmoothingMode = SmoothingMode.AntiAlias; StringFormat format = StringFormat.GenericDefault; List<WordPos> wordPosList = new List<WordPos>(); //需要副本
List<Point> areaPointList = Utils.GetAreaPointList().Select(m => m).ToList();
List<Point> verAreaPointList = Utils.GetVerAreaPointList(); List<int> fontSizeList = Utils.GetFontSizeList(Config.FontSize);
List<Color> fontColorList = Utils.GetFontColorList(Config.FontColor);
List<string> fontFamilyList = Utils.GetFontFamilyList(Config.FontFamily);
List<int> verifyLengthList = Utils.GetVerifyLengthList(Config.VerifyLength);
List<string> tipFontFamilyList = Utils.GetTipFontFamilyList(Config.TipFontFamily);
List<Color> tipFontColorList = Utils.GetTipFontColorList(Config.TipFontColor); var verifyLengthIndex = rnd.Next(, verifyLengthList.Count); for (int i = ; i < randWordList.Count; i++)
{
int sizeIndex = rnd.Next(, fontSizeList.Count);
int colorIndex = rnd.Next(, fontColorList.Count);
int familyIndex = rnd.Next(, fontFamilyList.Count);
int areaPointIndex = rnd.Next(, areaPointList.Count); Font font = new Font(fontFamilyList[familyIndex], fontSizeList[sizeIndex], FontStyle.Bold, GraphicsUnit.Pixel);
SizeF sizef = picture.MeasureString(randWordList[i], font); Point point = areaPointList[areaPointIndex];
areaPointList.RemoveAt(areaPointIndex); Rectangle rect = new Rectangle(point.X, point.Y, (int)sizef.Width, (int)sizef.Height); using (GraphicsPath path = GetStringPath(randWordList[i], rect, font, format))
{ RectangleF off = rect;
off.Offset(, );//阴影偏移
using (GraphicsPath offPath = GetStringPath(randWordList[i], off, font, format))
{
Brush b = new SolidBrush(Color.White);
picture.FillPath(b, offPath);
b.Dispose();
} SolidBrush semiTransBrush = new SolidBrush(fontColorList[colorIndex]);
picture.DrawPath(Pens.WhiteSmoke, path);//绘制轮廓(描边)
picture.FillPath(semiTransBrush, path);//填充轮廓(填充)
}
wordPosList.Add(new WordPos()
{
Word = randWordList[i],
X = point.X,
Y = point.Y,
Width = sizef.Width,
Height = sizef.Height
});
} //输出需要验证的字
List<WordPos> verWordList = wordPosList.Take(verifyLengthList[verifyLengthIndex]).ToList();
for (int i = ; i < verAreaPointList.Count && i < verWordList.Count; i++)
{
int colorIndex = rnd.Next(, tipFontColorList.Count);
int tipFamilyIndex = rnd.Next(, tipFontFamilyList.Count); Font font = new Font(tipFontFamilyList[tipFamilyIndex], Config.TipFontSize, FontStyle.Bold, GraphicsUnit.Pixel);
SizeF sizef = picture.MeasureString(randWordList[i], font); Point point = verAreaPointList[i];
Rectangle rect = new Rectangle(point.X, point.Y, (int)sizef.Width, (int)sizef.Height); var color = tipFontColorList[colorIndex];
using (GraphicsPath path = GetStringPath(randWordList[i], rect, font, format))
{
SolidBrush semiTransBrush = new SolidBrush(color);
if ((color.R + color.G + color.B) > )
{
picture.DrawPath(Pens.Black, path);
}
else
{
picture.DrawPath(Pens.White, path);
}
picture.FillPath(semiTransBrush, path);
}
}
return verWordList;
} private static GraphicsPath GetStringPath(string s, RectangleF rect, Font font, StringFormat format)
{
GraphicsPath path = new GraphicsPath();
path.AddString(s, font.FontFamily, (int)font.Style, font.Size, rect, format); return path;
}

其中需要注意的是,汉字的水印位置,我的作法是将背景图片先划分成X个区域,然后画一个区域,将其排除掉。

第五步:生成图片

        public void Create()
{
_httpContext.Response.CacheControl = "no-cache";
_httpContext.Session["VCode_ISValidate"] = false; List<string> chineseWordList = Utils.GetChineseWordList();
List<string> backGroundImageList = Utils.GetBackGroundImageList(_httpContext.Server.MapPath(Config.BackGroundImageDir)); var randWordList = Utils.GetRandChineseWordList(chineseWordList, Config.RandLength); Random rnd = new Random(unchecked((int)DateTime.Now.Ticks));
var imgIndex = rnd.Next(, backGroundImageList.Count); string imagePath = backGroundImageList[imgIndex];
using (Image img = Image.FromFile(imagePath))
{
using (Graphics g = Graphics.FromImage(img))
{
using (Bitmap bmp = new Bitmap(imagePath))
{
List<WordPos> verWordList = Utils.DrawWord(g, bmp, randWordList, rnd);
_httpContext.Session["VCode_Word"] = verWordList;
}
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
_httpContext.Response.ClearContent();
_httpContext.Response.ContentType = "image/Jpeg";
_httpContext.Response.BinaryWrite(ms.ToArray());
}
}
}

最后,验证环节

JS来获取到相应的位置坐标传到后端后,通过比较相应的两点距离在一定范围内则算通过。

 public bool Validate(dynamic pos)
{
bool isValidate = false;
_httpContext.Session["VCode_ISValidate"] = false;
if (pos.Length > && _httpContext.Session["VCode_Word"] != null)
{
bool isNotMath = false;
List<WordPos> verWordList = (List<WordPos>)_httpContext.Session["VCode_Word"];
_httpContext.Session["VCode_ISValidate"] = true;
_httpContext.Session["VCode_Word"] = null; if (pos.Length == verWordList.Count)
{
for (int i = ; i < pos.Length; i++)
{
var _posLeft = Convert.ToInt32(pos[i]["left"]) + / ;
var _posTop = Convert.ToInt32(pos[i]["top"]) + / ;
Point _posCenter = new Point(_posLeft, _posTop); var _ckLeft = Convert.ToInt32(verWordList[i].X + verWordList[i].Width / );
var _ckTop = Convert.ToInt32(verWordList[i].Y + verWordList[i].Height / ); Point _ckPosCenter = new Point(_ckLeft, _ckTop); if (Utils.GetDistance(_posCenter, _ckPosCenter) > )
{
isNotMath = true;
break;
}
}
if (isNotMath == false)
{
isValidate = true;
}
}
}
return isValidate;
}

当然,实际上QQ注册的需要验证字都是一个词组,要做到这一点可能只有做一个词库才能完成了。

仿QQ注册验证码的实现。的更多相关文章

  1. C# 实现简单仿QQ登陆注册功能

    闲来没事,想做一个仿QQ登陆注册的winform,于是利用工作之余,根据自己的掌握和查阅的资料,历时4天修改完成,新手水平,希望和大家共同学习进步,有不同见解希望提出! 废话不多说,进入正题: 先来看 ...

  2. < JAVA - 大作业(2)仿qq即时通讯软件 >

    < JAVA - 大作业(2)仿qq即时通讯软件 > 背景 JAVA上机大作业:设计一个仿qq即时通讯软件 任务简要叙述:设计一款仿QQ的个人用户即时通讯软件,能够实现注册,登陆,与好友聊 ...

  3. SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)

     SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...

  4. Socket实现仿QQ聊天(可部署于广域网)附源码(1)-简介

    1.前言 本次实现的这个聊天工具是我去年c#程序设计课程所写的Socket仿QQ聊天,由于当时候没有自己的服务器,只能在机房局域网内进行测试,最近在腾讯云上买了一台云主机(本人学生党,腾讯云有个学生专 ...

  5. 高仿QQ的即时通讯应用带服务端软件安装

    Android 基于xmpp协议,smack包,openfire服务端(在下面)的高仿QQ的即时通讯实现.实现了注册,登录,读取好友列表,搜索好友,添加分组,添加好友,删除好友,修改心情,两个客户端之 ...

  6. 高仿QQ即时聊天软件开发系列之三登录窗口用户选择下拉框

    上一篇高仿QQ即时聊天软件开发系列之二登录窗口界面写了一个大概的布局和原理 这一篇详细说下拉框的实现原理 先上最终效果图 一开始其实只是想给下拉框加一个placeholder效果,让下拉框在未选择未输 ...

  7. 高仿QQ即时聊天软件开发系列之二登录窗口界面

    继上一篇高仿QQ即时聊天软件开发系列之一开端之后,开始做登录窗口 废话不多说,先看效果,只有界面 可能还有一些细节地方没有做,例如那个LOGO嘛,不要在意这些细节 GIF虽短,可是这做起来真难,好吧因 ...

  8. WPF ”真正的“高仿QQ

    时常可以在各种论坛 博客 看到 各种所谓的 高仿QQ. 说实话 越看越想笑呢.(PS:纯粹的 抨击 那些 不追求 UI 完美主义者) 例如:       本次模仿 采用 C# WPF XAML , 总 ...

  9. 基于环信的仿QQ即时通讯的简单实现

    代码地址如下:http://www.demodashi.com/demo/11645.html 我的博客地址 之前一直想实现聊天的功能,但是感觉有点困难,今天看了环信的API,就利用下午的时间动手试了 ...

随机推荐

  1. UVaLive 6625 Diagrams & Tableaux (状压DP 或者 DFS暴力)

    题意:给一个的格子图,有 n 行单元格,每行有a[i]个格子,要求往格子中填1~m的数字,要求每个数字大于等于左边的数字,大于上边的数字,问有多少种填充方法. 析:感觉像个DP,但是不会啊...就想暴 ...

  2. UVaLive 7361 Immortal Porpoises (矩阵快速幂)

    题意:求Fibonacci的第 n 项. 析:矩阵快速幂,如果不懂请看http://www.cnblogs.com/dwtfukgv/articles/5595078.html 是不是很好懂呢. 代码 ...

  3. jQuery jsonp跨域请求

    跨域的安全限制都是对浏览器端来说的,服务器端是不存在跨域安全限制的. 浏览器的同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互. 如果协议,端口和主机对于两个页面是相同的,则两个页面具 ...

  4. C#中的强制类型转换与as转换的区别

    C#中的强制类型转换 例如有ClassA与ClassB两个类创建两个类的对象进行转换 1 2 ClassA a = new ClassA();  ClassB b = new ClassB(); 如果 ...

  5. Display:Block

    根据CSS规范的规定,每一个网页元素都有一个display属性,用于确定该元素的类型,每一个元素都有默认的display属性值,比如div元素,它的默认display属性值为“block”,成为“块级 ...

  6. 深入了解 Dojo 的服务器推送技术

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html 内部邀请码:C8E245J (不写邀请码,没有现金送) 国 ...

  7. 【android-cocos2d-X 环境配置】在Mac下搭建Cocos2d-X-android开发环境!

    转自:http://blog.csdn.net/dingkun520wy/article/details/17097593 (1)下载 首先要下载好要用到的东西: 1.android-SDK 地址是  ...

  8. 关于Vim的问题s

    2013-11-23 17:29:45 1.关于.swp文件 swap对于保护非正常退出是有好处的,但从最开始使用vim就发现的一个问题十分恼火!非正常退出再进入后选择了恢复R,然后编辑正常保存正常退 ...

  9. MyBatis之八:需要说明的几个java api的生命周期以及封装

    学习mybatis不得不了解SqlSessionFactoryBuilder.SqlSessionFactory.SqlSession.这里主要是讲解它们的生命周期以及一般最佳实践. 一般来说对象的生 ...

  10. 怎样在osg中动态的设置drawable的最近最远裁剪面

    // draw callback that will tweak the far clipping plane just    // before rendering a drawable.    s ...