C#实现图像选择验证码

news2024/12/28 15:37:19

开发环境:C#,VS2019,.NET Core 3.1,ASP.NET Core

前几年使用12306购买火车票时使用过这种验证码,根据文字描述选择对应的图片,文字是随机的,图片也是随机的。

1、建立一个验证码控制器

新建两个方法Create和Check,Create用于创建验证码,Check用于验证它是否有效。

声明一个静态类变量存放列表,列表中存放包含令牌和验证码的对象。

        /// <summary>
        /// 返回多张图片,一个文字和令牌.
        /// </summary>
        /// <returns></returns>
        public string Create()
        {
            try
            {
                VCodeImageSelectModel model = new VCodeImageSelectModel();
                model.id = Guid.NewGuid().ToString();    // 生成令牌
                var vcode = VCodeImageSelectModel.GetVCode();    // 生成验证码
                model.code.text = vcode.Item1;
                model.code.image = vcode.Item4;
                _list.Add(model);

                // 返回结果
                var image = VCodeImageSelectModel.DrawImage(model.code.text);
                var base64 = VCodeImageSelectModel.BitmapToBase64Str(image);
                var images = vcode.Item2;
                VCodeImageSelectController_Create_Receive result = new VCodeImageSelectController_Create_Receive();
                result.code = "0";
                result.data.id = model.id;
                result.data.img = VCodeImageSelectModel.BitmapToBase64Str(images);
                result.data.img_index = vcode.Item3;
                result.data.img_text = base64;
                var json = JsonConvert.SerializeObject(result);
                return json;
            }
            catch (Exception ex)
            {
                _logger.LogWarning(exception: ex, message: ex.Message);
                VCodeImageSelectController_Create_Receive result = new VCodeImageSelectController_Create_Receive();
                result.code = "999999";
                result.msg = "系统异常";
                var json = JsonConvert.SerializeObject(result);
                return json;
            }
        }

        /// <summary>
        /// 检查验证码是否有效
        /// </summary>
        /// <param name="id">令牌.</param>
        /// <param name="code">验证码.</param>
        /// <returns></returns>
        [HttpGet]
        public string Check(string id, string code)
        {
            try
            {
                ReceiveObject result = new ReceiveObject();
                var list_result = code.Split(',').ToList();    // 拆分验证码为数组
                var list_temp = new List<string>(); 
                if(_list.Find(m => m.id == id) == null)
                {
                    result.code = "999999";
                    result.msg = "系统异常";
                    var json = JsonConvert.SerializeObject(result);
                    return json;
                }

                _list.Find(m => m.id == id).code.image.ForEach(m =>
                {
                    // 复制数组,避免重复提交后仍然能获取到完整的数据
                    list_temp.Add(m);
                });

                var index = _list.FindIndex(m =>
                {
                    var flag = false;
                    for (int i = 0; i < list_result.Count; i++)
                    {
                        var item = list_result[i];
                        flag = m.code.image.Exists(m => m.Equals(item));
                        if(flag == false)
                        {
                            // 选中了错误的
                            return false;
                        }
                        else
                        {
                            // 每次将选中的项删除,这样最终得到的数组应该是空的
                            list_temp.Remove(item);
                        }
                    }
                    
                    if(list_temp.Count > 0)
                    {
                        // 未全部选中
                        return false;
                    }

                    if (m.id.Equals(id) && flag)
                    {
                        // 通过
                        return true;
                    }

                    return false;
                });

                if (index >= 0)
                {
                    _list.RemoveAt(index);
                    result.code = "0";
                    result.msg = "验证成功";
                    var json = JsonConvert.SerializeObject(result);
                    return json;
                }
                else
                {
                    result.code = "1";
                    result.msg = "验证失败";
                    var json = JsonConvert.SerializeObject(result);
                    return json;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(exception: ex, message: ex.Message);
                ReceiveObject result = new ReceiveObject();
                result.code = "999999";
                result.msg = "系统异常";
                var json = JsonConvert.SerializeObject(result);
                return json;
            }
        }

2、建立一个验证码模型

验证码模型类包括:令牌和验证码属性。

再创建一个类存放GetVCode方法返回的对象包括:图片的类型(它又叫文字描述)的图片,图片数组,图片对应的序号,该类型的图片对应的序号。

DrawImage方法生成图片的类型(它又叫文字描述)的图片

GetVCodeList方法生成图片数组,图片对应的序号,该类型的图片对应的序号。

BitmapToBase64Str方法用来将图片对象转成Base64字符串

        /// <summary>
        /// 获取随机的文字和验证码.
        /// </summary>
        /// <returns>
        ///  第一个参数 - 图片的类型(文字描述)
        ///  第二个参数 - 图片数组,用来显示图片
        ///  第三个参数 - 图片对应的序号
        ///  第四个参数 - 该类型的图片对应的序号
        /// </returns>
        public static Tuple<string, List<Bitmap>, List<string>, List<string>> GetVCode()
        {
            Random random = new Random();
            var type = random.Next(1, List_Text.Count + 1).ToString();
            var typeName = List_Text.ElementAt(Convert.ToInt32(type) - 1);
            var result = GetVCodeList(type);
            return new Tuple<string, List<Bitmap>, List<string>, List<string>>(typeName, result.Item1, result.Item2, result.Item3);
        }

        /// <summary>
        /// 获取随机的验证码.
        /// </summary>
        /// <param name="type">图片的类型.</param>
        /// <returns>
        ///  第一个参数 - 图片数组,用来显示图片
        ///  第二个参数 - 图片对应的序号
        ///  第三个参数 - 该类型的图片对应的序号
        /// </returns>
        private static Tuple<List<Bitmap>, List<string>, List<string>> GetVCodeList(string type)
        {
            // 这里的随机码是一个有四个元素的数组,如果发现没有生成指定类型的,就重新生成.
            var list_files = Directory.GetFiles(PathHelper.Path + @"Images\imageSelect");
            var count = list_files.Count();
            List<string> list_index = new List<string>();
            var list_fileName = new List<string>();
            List<string> list_selectedIndex = new List<string>();
            Random random = new Random();
            while (true)
            {
                while (true)
                {
                    var index = random.Next(0, count).ToString();
                    if (list_index.Exists(m => m.Equals(index)) == false)
                    {
                        list_index.Add(index);
                        var temp = list_files.ElementAt(Convert.ToInt32(index)).Replace(PathHelper.Path + @"Images\imageSelect", "");    // 只保留文件名,去掉路径
                        list_fileName.Add(temp);
                        if(temp.Replace("\\img", "").Substring(0, 1) == type)
                        {
                            list_selectedIndex.Add(index);
                        }
                    }

                    if (list_index.Count >= 4)
                    {
                        break;
                    }
                }

                // 判断是否至少生成了一个指定类型的图片
                var flag = false;
                flag = list_fileName.Exists(m =>
                {
                    if (m.Contains("img" + type))
                    {
                        return true;
                    }

                    return false;
                });

                if (flag == false)
                {
                    list_index.Clear();
                    list_fileName.Clear();
                    list_selectedIndex.Clear();
                    continue;
                }
                else
                {
                    // 至少生成了一个指定类型的图片
                    break;
                }
            }

            // 加载图片
            List<Bitmap> list_image = new List<Bitmap>();
            for (int i = 0; i < list_fileName.Count; i++)
            {
                var image = Image.FromFile(string.Format(@"{0}Images\imageSelect\{1}", PathHelper.Path, list_fileName.ElementAt(i)));
                list_image.Add((Bitmap)image);
            }

            return new Tuple<List<Bitmap>, List<string>, List<string>>(list_image, list_index, list_selectedIndex);
        }

        /// <summary>
        /// 将图片对象转成Base64的字符串.
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public static List<string> BitmapToBase64Str(List<Bitmap> bitmap)
        {
            List<string> list = new List<string>();
            for (int i = 0; i < bitmap.Count; i++)
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    bitmap.ElementAt(i).Save(memoryStream, ImageFormat.Jpeg);
                    byte[] bytes = memoryStream.ToArray();
                    list.Add(Convert.ToBase64String(memoryStream.ToArray()));
                }
            }

            return list;
        }

        /// <summary>
        /// 将图片对象转成Base64的字符串.
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public static string BitmapToBase64Str(Bitmap bitmap)
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                bitmap.Save(memoryStream, ImageFormat.Jpeg);
                byte[] bytes = memoryStream.ToArray();
                return Convert.ToBase64String(memoryStream.ToArray());
            }
        }

        /// <summary>
        /// 绘制验证码的图片.
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public static Bitmap DrawImage(string code)
        {
            Color[] list_color =
            {
                Color.FromArgb(240, 230, 140),    // 黄褐色(亮)
                Color.FromArgb(138, 54, 15),    // 黄褐色(暗)
                Color.FromArgb(51, 161, 201),    // 蓝色(亮)
                Color.FromArgb(25, 25, 112),    // 蓝色(暗)
                Color.FromArgb(192, 192, 192),    // 灰白(亮)
                Color.FromArgb(128, 128, 105),    // 灰白(暗)
            };

            Random random = new Random();

            // 创建画板
            Bitmap bitmap = new Bitmap(85, 50);

            // 创建画笔
            Graphics grp = Graphics.FromImage(bitmap);
            grp.Clear(Color.White);    // 设置背景色为白色

            // 绘制噪点
            for (int i = 0; i < random.Next(30, 40); i++)
            {
                int x = random.Next(bitmap.Width);
                int y = random.Next(bitmap.Height);
                grp.DrawLine(new Pen(Color.LightGray, 1), x, y, x + 1, y);
            }

            // 绘制随机的线条
            grp.DrawLine(
                new Pen(list_color[random.Next(list_color.Length)], random.Next(3)),
                new Point(random.Next(bitmap.Width / 2), random.Next(bitmap.Height / 2)),
                new Point(bitmap.Width / 2 + random.Next(bitmap.Width / 2), bitmap.Height / 2 + random.Next(bitmap.Height / 2))
            );

            // 绘制验证码
            for (int i = 0; i < code.Length; i++)
            {
                var item = code[i];
                grp.DrawString(item.ToString(),
                    new Font(FontFamily.GenericSansSerif, 25, FontStyle.Bold),
                    new SolidBrush(list_color[random.Next(list_color.Length)]),
                    x: (75 / 2) * i,
                    y: random.Next(5));
            }

            // 在验证码上绘制噪点
            for (int i = 0; i < random.Next(15, 25); i++)
            {
                int x = random.Next(bitmap.Width);
                int y = random.Next(bitmap.Height);
                grp.DrawLine(new Pen(list_color[random.Next(list_color.Length)], 1), x, y, x + 1, y);
            }

            // 绘制随机的线条
            grp.DrawLine(
                new Pen(list_color[random.Next(list_color.Length)], random.Next(3)),
                new Point(random.Next(bitmap.Width / 2), random.Next(bitmap.Height / 2)),
                new Point(bitmap.Width / 2 + random.Next(bitmap.Width / 2), bitmap.Height / 2 + random.Next(bitmap.Height / 2))
            );

            return bitmap;
        }

3、新建一个视图文件,引入jquery,css文件,js方法中添加几个事件 - 点击图片,提交按钮。页面首次加载时调用控制器的Create方法获取图片和令牌。

<link href="~/css/image_select.css" rel="stylesheet" />
<!-- 展示验证码 -->
<div class="container">
    <div class="main">
        <div>
            请选择所有的<img id="backTextImage" src="" alt="">
        </div>
        @for (int i = 1; i < 5; i++)
        {
            @if (i % 2 == 1)
            {
                <div style="float:left;">
                    <div>
                        <img id="backImage@(i)" + src="" class="img_check" checked="0">
                    </div>
                    <div id="div_icon@(i)" class="divIconClass">
                        <img id="imgIcon@(i)" src="~/img/Select.png" class="imgIconClass"/>
                    </div>
                </div>
                
            }
            else
            {
                <div>
                    <div>
                        <img id="backImage@(i)" + src="" class="img_check" checked="0">
                    </div>
                    <div id="div_icon@(i)" class="divIconClass">
                        <img id="imgIcon@(i)" src="~/img/Select.png"  class="imgIconClass"/>
                    </div>
                </div>
            }
        }
    </div>
    <div style="position:relative;left:20px;">
        <input type="button" value="提交" id="button_submit" />
    </div>
</div>

<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/js/image_select.js"></script>
* {
    margin: 0;
    padding: 0;
}

.main {
    position: relative;
    margin-left: 20px;
    margin-top: 20px;
    width: 300px;
    background-color: white;
}

.img_check {
    width: 100px;
    height: 100px;
}

#div_icon1 {
    bottom: 30px;
    left: 70px;
}

#div_icon2 {
    bottom: 56px;
    left: 168px;
}

#div_icon3 {
    bottom: 30px;
    left: 70px;
}

#div_icon4 {
    bottom: 56px;
    left: 168px;
}

.divIconClass {
    position: relative;
    width: 25px;
    height: 25px;
}

.imgIconClass {
    width: 1px;
    height: 1px;
    left: 70px;
}


// 图片列表
var _imageBase64 = new Array();

// 文字描述的图片
var _imageText = new String();

// 令牌
var _id;

/**
 * 设置当前图片
 * @param {any} imageText 描述文字的图片
 * @param {any} imageBase64 图片列表
 * @param {any} img_index 图片的序号
 */
function setCurrentImageBase64(imageText, imageBase64, img_index)
{
    // 显示描述文字
    _imageText = 'data:image/webp;base64,' + imageText;
    document.getElementById('backTextImage').src = _imageText;

    // 显示图片
    for (var i = 0; i < imageBase64.length; i++)
    {
        _imageBase64[i] = 'data:image/webp;base64,' + imageBase64[i];
        document.getElementById('backImage' + (i + 1)).src = _imageBase64[i];
        document.getElementById('backImage' + (i + 1)).attributes["val1"] = img_index[i];
    }
}

/**
 * 验证方法
 * @param {any} code 验证码
 */
function check(code)
{
    $.get("Check?code=" + code + "&id=" + _id, function (data) {
        var obj = JSON.parse(data);
        if (obj.code == "0") {
            alert("验证成功");
        }
        else {
            alert("验证失败");
        }

        location.reload();
    });
}

window.onload = function () {
    $.get("Create", function (data) {
        // 获取图片和令牌
        var obj = JSON.parse(data);
        _id = obj.data.id;

        // console.log(obj.data.img_index.length);
        setCurrentImageBase64(obj.data.img_text, obj.data.img, obj.data.img_index);
    });

    // 点击图片
    $(".img_check").click(function (event) {
        var id = event.currentTarget.id;
        var index = id.replace("backImage", "");

        // console.log(event.currentTarget.attributes["val1"]);
        if (event.currentTarget.attributes["checked"].value == "0")
        {
            // 选中状态
            event.currentTarget.attributes["checked"].value = "1";
            $("#imgIcon" + index).css("height", "25");
            $("#imgIcon" + index).css("width", "25");
        }
        else
        {
            // 取消选中状态
            event.currentTarget.attributes["checked"].value = "0";
            $("#imgIcon" + index).css("height", "1");
            $("#imgIcon" + index).css("width", "1");
        }
    });

    // 提交按钮
    $("#button_submit").click(function () {
        var result = "";
        var v1 = $("#backImage1");
        var v2 = $("#backImage2");
        var v3 = $("#backImage3");
        var v4 = $("#backImage4");
        if (v1[0].attributes["checked"].value == "1") {
            var code = v1[0].attributes["val1"];
            result = result + code + ","
        }

        if (v2[0].attributes["checked"].value == "1") {
            var code = v2[0].attributes["val1"];
            result = result + code + ","
        }

        if (v3[0].attributes["checked"].value == "1") {
            var code = v3[0].attributes["val1"];
            result = result + code + ","
        }

        if (v4[0].attributes["checked"].value == "1") {
            var code = v4[0].attributes["val1"];
            result = result + code + ","
        }

        if (result.length <= 0) {
            alert("请选择图片");
            return;
        }
        else {
            result = result.substring(0, result.length - 1);
        }

        check(result);
    })
}

效果图:

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/887814.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年国赛数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 最短时…

保姆级别讲解Python数据处理,你绝对能会

名字&#xff1a;阿玥的小东东 学习&#xff1a;Python、C/C 主页链接&#xff1a;阿玥的小东东的博客_CSDN博客-python&&c高级知识,过年必备,C/C知识讲解领域博主 目录 1. 文件读取 2. 数据处理 3. 处理结果输出 总的来说 为了咱们让程序跑起来&#xff0c;我们需…

如何使用CSS实现一个瀑布流布局?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用CSS实现瀑布流布局⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚…

前端笔记2023

border-radius: 数值px;圆角 flex布局 小程序中 display:flex; //flex布局 flex-direction:row/column; //规定主轴的方向&#xff1a;row/column justify-content:space-around; //元素在主轴方向的排列方式&#xff1a;flex-start/flex-end/space-around/space-betwee…

【动画】p61Mixamo自动绑定骨骼

Mixamo自动绑定骨骼 Mixamo Mixamo 虚幻商城搜索Mixamo Animation Retargeting 有两个版本4.14-4.27和5.0-5.2 优化&#xff1a; 程序方面优化 素材方面优化 光照方面优化 模型面数优化 材质方面优化 先获得一个模型 打开mixamo网站 导入模型 自动进入自动绑骨界面&#xf…

工厂方法模式【Factory Method Pattern】

前言 1.工厂模式概念 实例化对象&#xff0c;用工厂方法代替new操作(重点) 工厂模式包括工厂方法模式和抽象工厂模式 抽象工厂模式是工厂方法模式的扩展 2.什么情况下适合工厂模式 有一组类似的对象需要创建 在编码时不能预见需要创建哪种类的实例 系统需要考虑扩展性&#xff…

前端(十二)——深入理解和使用 async和await

&#x1f61b;博主&#xff1a;小猫娃来啦 &#x1f61b;文章核心&#xff1a;深入理解和使用 async和await 在 JS中&#xff0c;异步操作是无法避免的&#xff0c;而处理异步操作最常用的方法是使用回调函数或者 Promise。然而&#xff0c;自 ES2017 引入了 async/await 之后…

分布式版本控制系统(一)

分布式版本控制系统(一) 目录 分布式版本控制系统(一) 1、Git、Github、Gitlab 的区别2、Git 与 SVN 区别3、Git工作流程4、Git基本概念5、Git 客户端安装使用 5.1 git-server安装配置5.2 git-client配置免密登录git服务器5.3 文本编辑器5.4 差异分析工具5.5 查看配置信息5.6 常…

clip模型学习

先介绍几个相关概念&#xff1a; 1.零样本学习&#xff08;zero-shot&#xff09; 参考&#xff1a;https://blog.csdn.net/gary101818/article/details/129108491 利用训练集数据训练模型&#xff0c;使得模型能够对测试集的对象进行分类&#xff0c;但是训练集类别和测试集类…

【枚举】CF1858 B

Problem - B - Codeforces 题意&#xff1a; 思路&#xff1a; 直接枚举是去掉哪个店&#xff0c;然后计算贡献即可 虽然赛时做出来了&#xff0c;但是这种计算还是感觉不熟练 Code&#xff1a; #include <bits/stdc.h>#define int long longusing i64 long long;co…

订货系统怎么选?从这四个方面筛选错不了(一)

选择适合的订货系统对企业来说是一个重要且复杂的决策。一个优秀的订货系统可以提高供应链的运作效率、降低成本&#xff0c;并帮助企业更好地管理库存和订单。如果不知道从那几方面做选择&#xff0c;我们可以简单从四个方面进行筛选&#xff0c;这样一般错不了&#xff0c;今…

【RT-Thread】 启用hash算法软件包

参考文章 哈希匹配算法在单片机上的应用 近来单片机开发因业务需求需要用hash算法&#xff0c;为开启此功能&#xff0c;在RT-Thread Studio环境下操作如下&#xff1a; 1&#xff0c;在RT-Thread Setttings里面的搜索栏里输入hash,然后定位到下图所示 使能hash match for …

四、Controller 配置总结、RestFul 风格

文章目录 一、Controller 配置总结二、RestFul 风格2.1 使用 RequestMapping 的 method 属性指定请求类型 三、扩展&#xff1a;小黄鸭调试法 一、Controller 配置总结 实现 Controller 控制器的方式 实现 Controller 接口&#xff0c;重写 handleRequest 方法实现 控制器实现 …

IDEA常用工具配置

IDEA常用工具&配置 如果发现插件市场用不了&#xff0c;可以设置Http Proxy&#xff0c;在该界面上点击”Check connection“并输入的地址&#xff1a;https://plugins.jetbrains.com/ 。 一、常用插件 1、MybatisX Mybaits Plus插件&#xff0c;支持java与xml互转 2、F…

【数据结构】_7.二叉树概念与基本操作

目录 1.树形结构 1.1 树的概念 1.2 树的相关概念 1.3 树的表示 1.4 树在实际中的应用—表示文件系统的目录树结构 ​编辑​2.二叉树 2.1 概念 2.2 特殊二叉树 2.3 二叉树的性质 2.4 二叉树的存储结构 2.4.1 顺序存储结构&#xff08;数组存储结构&#xff09; 2.4.2…

小程序数据可视化:使用图表和可视化工具展示数据

在当今信息爆炸的时代&#xff0c;数据无疑是最珍贵的资源之一。然而&#xff0c;海量的数据如果不加以整理和展示&#xff0c;很难从中获取有价值的信息。这时候&#xff0c;数据可视化就发挥了重要作用&#xff0c;它能够通过图表和可视化工具将复杂的数据转化为直观的视觉形…

Azure控制台添加磁盘到VM

在控制台中添加磁盘 RDP方式登录windows虚拟机&#xff0c;打开后提示连接&#xff0c;点击连接&#xff0c;并会指向server manager,点击File and Storage Services,然后点击Disks,并按照提示一步一步操作 3. 最后在我的电脑中可以看到新创建的磁盘

[Go版]算法通关村第十一关白银——位运算的高频算法题

目录 专题1&#xff1a;位移的妙用题目&#xff1a;位1的个数&#xff08;也被称为汉明重量&#xff09;解法1&#xff1a;遍历所有位&#xff0c;判断每个位的数字是否是1Go代码 解法2&#xff1a;依次消除每个1的位 numnum&(num-1)Go代码 题目&#xff1a;比特位计数思路…

春秋云镜 CVE-2020-21650

春秋云镜 CVE-2020-21650 MyuCMS后台rce 靶标介绍 MyuCMS开源内容管理系统,采用ThinkPHP开发而成的社区商城聚合&#xff0c;插件&#xff0c;模板&#xff0c;轻便快捷容易扩展 其2.2版本中admin.php/config/add方法存在任意命令执行漏洞。 启动场景 漏洞利用 exp /index…

以安全促发展——《数据出境安全评估办法》解读

各国关于数据出境的监管要求一直是各国数据监管的风向标&#xff0c;不仅体现国家对于数据安全的重视程度&#xff0c;也能意会出国家对于数据竞争的态度以及数字经济发展的思路。例如欧盟《通用数据保护条例》&#xff08;GDPR&#xff09;设定的个人数据出境的限制&#xff0…