C#实现旋转图片验证码

  • Post author:
  • Post category:其他


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

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

新建两个方法Create和Check,Create用于创建验证码(返回1张图片和令牌),Check用于验证(验证图片旋转角度)它是否有效。

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

        /// <summary>
        /// 返回一张图片和令牌.
        /// </summary>
        /// <returns></returns>
        public string Create()
        {
            try
            {
                // 记录验证码到缓存中
                VCodeCircleModel model = new VCodeCircleModel();
                model.id = Guid.NewGuid().ToString();    // 生成令牌
                var vcode = VCodeCircleModel.GetVCode();    // 生成验证码
                model.code = vcode;
                _list.Add(model);

                // 返回图片
                var images = VCodeCircleModel.GetImage(Convert.ToInt32(vcode));
                VCodeCircleController_Create_Receive result = new VCodeCircleController_Create_Receive();
                result.code = "0";
                result.data.id = model.id;
                result.data.img = VCodeCircleModel.BitmapToBase64Str(images);
                var json = JsonConvert.SerializeObject(result);
                return json;
            }
            catch (Exception ex)
            {
                _logger.LogWarning(exception: ex, message: ex.Message);
                VCodeCircleController_Create_Receive result = new VCodeCircleController_Create_Receive();
                result.code = "999999";
                result.msg = "系统异常";
                var json = JsonConvert.SerializeObject(result);
                return json;
            }
        }


        public string Check(string id, string code)
        {
            try
            {
                // 旋转图片的误差在±5
                var temp = Convert.ToInt32(code) - 5;
                var index = _list.FindIndex(m =>
                {
                    if (m.id.Equals(id))
                    {
                        for (int i = 0; i < 10; i++)
                        {
                            if (m.code.Equals(temp.ToString()))
                            {
                                return true;
                            }

                            temp++;
                        }
                    }

                    return false;
                });

                ReceiveObject result = new ReceiveObject();
                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;
            }
        }

    public class VCodeCircleModel
    {
        /// <summary>
        /// 令牌.
        /// </summary>
        public string id { get; set; }

        /// <summary>
        ///验证码.
        /// </summary>
        public string code { get; set; }

        /// <summary>
        /// 获取随机验证码.
        /// </summary>
        /// <returns></returns>
        public static string GetVCode()
        {
            // 这里的随机码是旋转图片的角度,至少旋转60度,最多旋转300度
            Random random = new Random();
            return random.Next(60, 300).ToString();
        }

        /// <summary>
        /// 随机获取一张图片.
        /// </summary>
        /// <returns></returns>
        public static Bitmap GetImage(int angle)
        {
            // 从文件加载原图
            Random random = new Random();
            var image_index = random.Next(0, 2);
            Image originImage;
            switch (image_index)
            {
                case 0:
                    originImage = Image.FromFile(string.Format(@"{0}\Images\{1}", PathHelper.Path, "circleImg1.png"));
                    break;

                case 1:
                default:
                    originImage = Image.FromFile(string.Format(@"{0}\Images\{1}", PathHelper.Path, "circleImg2.png"));
                    break;
            }

            originImage = Rotate(originImage as Bitmap, angle);
            return (Bitmap)originImage;
        }

        /// <summary>
        /// 图片旋转
        /// </summary>
        /// <param name="ImageOriginal">原图.</param>
        /// <param name="AngleValue">旋转角度.</param>
        /// <returns></returns>
        public static Bitmap Rotate(Bitmap ImageOriginal, float AngleValue)
        {
            AngleValue = AngleValue % 360;
            double radian = AngleValue * Math.PI / 180.0;
            double cos = Math.Cos(radian);
            double sin = Math.Sin(radian);
            int w = ImageOriginal.Width;
            int h = ImageOriginal.Height;
            int W = (int)(Math.Max(Math.Abs(w * cos - h * sin), Math.Abs(w * cos + h * sin)));
            int H = (int)(Math.Max(Math.Abs(w * sin - h * cos), Math.Abs(w * sin + h * cos)));
            Bitmap ImageBaseOriginal = new Bitmap(W, H, PixelFormat.Format32bppArgb);
            System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(ImageBaseOriginal);
            g.InterpolationMode = InterpolationMode.NearestNeighbor;
            g.SmoothingMode = SmoothingMode.HighQuality;
            Point Offset = new Point((W - w) / 2, (H - h) / 2);
            Rectangle rect = new Rectangle(Offset.X, Offset.Y, w, h);
            Point center = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
            g.Clear(Color.White);
            g.TranslateTransform(center.X, center.Y);
            g.RotateTransform(360 - AngleValue);
            g.TranslateTransform(-center.X, -center.Y);
            g.DrawImage(ImageOriginal, rect);
            g.ResetTransform();
            g.Save();
            g.Dispose();
            return ImageBaseOriginal;
        }

        /// <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());
            }
        }
    }

2、新建一个视图文件,引入jquery,css文件,js方法中添加三个鼠标事件 – 鼠标按下,鼠标移动和鼠标松开。页面首次加载时调用控制器的Create方法获取图片和令牌,在鼠标松开时调用Check方法验证旋转的角度是否有效。

本来想实现鼠标在图片上拖动后让图片的效果,但发现难度有点大,就换成了拖动滑块后让图片旋转的形式。

<link href="~/css/circle_slide.css" rel="stylesheet" type="text/css" />

<!-- 展示验证码 -->
<div class="container">
    <div class="main">
        <div id="content" class="content">
            <img id="backImage" src="" alt="">
        </div>
    </div>
    <div id="slider">
        <div id="sliderBlock"></div>
    </div>
</div>

<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/js/MyCircleSlide.js"></script>

* {
    margin: 0;
    padding: 0;
}

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

.content {
    width: 100%;
}

    .content img {
        width: 100%;
        height: auto;
    }


#slider {
    width: 85%;
    height: 40px;
    background-color: aliceblue;
    position: relative;
}

#sliderBlock {
    position: absolute;
    left: 5px;
    height: 30px;
    width: 45px;
    top: 5px;
    background-color: white;
    border-radius: 5px;
    box-shadow: 0 0 10px 2px lightgray;
}

$(function () {
    var _imageBase64;    // 大图
    var _id;

    // 鼠标左键是否按下
    var isMouseDown;

    // 鼠标按下x值
    var mouseDownStartX;

    // 鼠标移动距离
    var mouseMoveLength;

    init();

    document.onmousedown = function (event) {
        var obj = getElementPosition(document.getElementById('sliderBlock'))
        if (event.clientX > obj.left &&
            event.clientX < (obj.left + obj.width) &&
            event.clientY > obj.top &&
            event.clientY < (obj.top + obj.height)) {
            // 鼠标点击事件发生在滑动条的范围内
            this.isMouseDown = true
            this.mouseDownStartX = event.clientX
            console.log("鼠标点击事件发生在滑动条的范围内");
        }
    }

    document.onmousemove = function (event) {
        if (this.isMouseDown) {
            this.mouseMoveLength = event.clientX - this.mouseDownStartX;    // 计算滑块拖动的距离
            if (this.mouseMoveLength > 0 &&
                this.mouseMoveLength < 360) {
                // 滑块拖动的最小距离是5px,最大距离是大图的宽度 - 小图的宽度
                document.getElementById('sliderBlock').style.left = 5 + this.mouseMoveLength + 'px'
                $("#backImage").css("transform", "rotate(" + (this.mouseMoveLength) + "deg)");
            }
        }
    }

    document.onmouseup = function (event) {
        // 鼠标松开后停止拖动
        if (this.isMouseDown) {
            this.isMouseDown = false
            // console.log("小图移动了:" + this.mouseMoveLength);
            // check(x.replace("px", ""));
            check(this.mouseMoveLength);
        }
    }

    // dom在浏览器的位置
    function getElementPosition(element) {
        let top = element.offsetTop
        let left = element.offsetLeft
        let width = element.offsetWidth
        let height = element.offsetHeight
        var currentParent = element.offsetParent;
        while (currentParent !== null) {
            top += currentParent.offsetTop
            left += currentParent.offsetLeft
            currentParent = currentParent.offsetParent
        }

        return { top, left, width, height }
    }

    function check(x) {
        $.get("Check?code=" + x + "&id=" + _id, function (data) {
            var obj = JSON.parse(data);
            if (obj.code == "0") {
                alert("验证成功");
            }
            else {
                alert("验证失败");
            }
        });
    }

    // 设置当前图片
    function setCurrentImageBase64(imageBase64) {
        _imageBase64 = imageBase64
        document.getElementById('backImage').src = _imageBase64
    }

    function init() {
        $.get("Create", function (data) {
            // 获取两张图片和令牌
            var obj = JSON.parse(data);
            _id = obj.data.id;
            setCurrentImageBase64('data:image/webp;base64,' + obj.data.img);
        });
    }
});

效果图:



版权声明:本文为xc917563264原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。