码迷,mamicode.com
首页 > 其他好文 > 详细

仿QQ注册验证码的实现。

时间:2016-03-31 16:30:18      阅读:222      评论:0      收藏:0      [点我收藏+]

标签:

最近发现一些网站的验证码全部换成了“极验”和“点触”的,发现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 = 16; area <= 55; area++)
                {
                    for (int place2 = (area == 55) ? 89 : 94, place = 1; 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();
            }));
        }
View Code

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

技术分享
        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;
            }));
        }
View Code

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

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

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

技术分享
        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(0, verifyLengthList.Count);

            for (int i = 0; i < randWordList.Count; i++)
            {
                int sizeIndex = rnd.Next(0, fontSizeList.Count);
                int colorIndex = rnd.Next(0, fontColorList.Count);
                int familyIndex = rnd.Next(0, fontFamilyList.Count);
                int areaPointIndex = rnd.Next(0, 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(1, 1);//阴影偏移
                    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 = 0; i < verAreaPointList.Count && i < verWordList.Count; i++)
            {
                int colorIndex = rnd.Next(0, tipFontColorList.Count);
                int tipFamilyIndex = rnd.Next(0, 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) > 382)
                    {
                        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;
        }
View Code

其中需要注意的是,汉字的水印位置,我的作法是将背景图片先划分成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(0, 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());
                }
            }
        }
View Code

 

最后,验证环节

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

技术分享
 public bool Validate(dynamic pos)
        {
            bool isValidate = false;
            _httpContext.Session["VCode_ISValidate"] = false;
            if (pos.Length > 0 && _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 = 0; i < pos.Length; i++)
                    {
                        var _posLeft = Convert.ToInt32(pos[i]["left"]) + 20 / 2;
                        var _posTop = Convert.ToInt32(pos[i]["top"]) + 23 / 2;
                        Point _posCenter = new Point(_posLeft, _posTop);

                        var _ckLeft = Convert.ToInt32(verWordList[i].X + verWordList[i].Width / 2);
                        var _ckTop = Convert.ToInt32(verWordList[i].Y + verWordList[i].Height / 2);

                        Point _ckPosCenter = new Point(_ckLeft, _ckTop);

                        if (Utils.GetDistance(_posCenter, _ckPosCenter) > 23)
                        {
                            isNotMath = true;
                            break;
                        }
                    }
                    if (isNotMath == false)
                    {
                        isValidate = true;
                    }
                }
            }
            return isValidate;
        }
View Code

 

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

 

仿QQ注册验证码的实现。

标签:

原文地址:http://www.cnblogs.com/dengxi/p/5341466.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!