C#实现图像选择验证码

开发环境: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);
    })
}

效果图:

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值