https://www.cnblogs.com/Easy999000/articles/17293649.html

前言
在之前的开发当中,一直使用.net自带的库生成图片验证码.老代码也使用了好多年了.但是到了.netcore时代..net要开始跨平台了.开发的系统要考虑到既可以部署到Windows系统上,也可以部署到Linux系统上(centeros,ubuntu).

.net内部自带的图形类库依赖于Windows的一些系统组件,想要同时兼容windows系统又要兼容Linux系统,变得非常困难.

.net已经在官方文档明确提出,不建议在Linux系统上使用.net自带的图形类库,但是在netcore6.0框架及之前,可以强行开启(报错不负责). netcore7.0及之后的版本,彻底放弃Linux的图形类库功能.

为了防止系统升级后功能受到影响,所以项目决定提前升级图片验证码的功能.

这里采用.net官方推荐的一个框架.SkiaSharp.

实现
验证码做了混淆处理,对文字的位置,角度,颜色,做了变换.

加入了带有颜色的干扰线.

可以实现简单的图片识别程序的防范.

/// <summary>
    /// 图片验证码
    /// </summary>
    public class ImageVerificationCode
    {
        public ImageVerificationCode(int Width, int Height)
        {
            // crate a surface
            var info = new SKImageInfo(Width, Height);

            Surface = SKSurface.Create(info);

            ///设置字体,解决乱码
            var fontManager = SKFontManager.Default;
            EmojiTypeface = fontManager.MatchCharacter('赵');

            Random1 = new Random();
        }
        SKSurface Surface;
        SKTypeface EmojiTypeface;
        Random Random1;

        /// <summary>
        /// 背景色
        /// </summary>
        public byte BackColorR = 185;
        /// <summary>
        /// 背景色
        /// </summary>
        public byte BackColorG = 185;
        /// <summary>
        /// 背景色
        /// </summary>
        public byte BackColorB = 185;
        /// <summary>
        /// 防靠色偏差值
        /// </summary>
        public byte BackOffset = 40;
        /// <summary>
        /// 字体大小
        /// </summary>
        int TextSize = 30;


        /// <summary>
        /// R限制范围
        /// </summary>
        public (byte begin, byte end, byte length) BoundsR
        {
            get
            {
                return Bounds(BackColorR);
            }
        }
        /// <summary>
        /// R限制范围
        /// </summary>
        public (byte begin, byte end, byte length) BoundsG
        {
            get
            {
                return Bounds(BackColorG);
            }
        }
        /// <summary>
        /// R限制范围
        /// </summary>
        public (byte begin, byte end, byte length) BoundsB
        {
            get
            {
                return Bounds(BackColorB);
            }
        }

        /// <summary>
        /// 颜色取值,限制范围
        /// </summary>
        /// <param name="Value"></param>
        /// <returns></returns>
        private (byte begin, byte end, byte length) Bounds(byte Value)
        {
            (byte begin, byte end, byte length) res = (0, 0, 0);

            res.begin = (byte)Math.Max(0, Value - BackOffset);
            res.end = (byte)Math.Min(255, Value + BackOffset);
            res.length = (byte)(res.end - res.begin);

            return res;
        }

        /// <summary>
        /// 清除图像,保留背景色
        /// </summary>
        public void Clear()
        {

            // the the canvas and properties
            var canvas = Surface.Canvas;
            // make sure the canvas is blank
            canvas.Clear(new SKColor(BackColorR, BackColorG, BackColorB));
        }
        /// <summary>
        /// 写文字
        /// </summary>
        /// <param name="Text"></param>
        public void WriteText(string Text)
        {
            var canvas = Surface.Canvas;
            var xPoint = 6;///x点
            var yPoint = (Surface.Canvas.DeviceClipBounds.Height + TextSize) / 2; ///y点
            // draw some text
            var paint = new SKPaint
            {
                Color = SKColors.Black,
                IsAntialias = true,
                Style = SKPaintStyle.Fill,
                TextAlign = SKTextAlign.Left,
                TextSize = TextSize,
                TextEncoding = SKTextEncoding.Utf8,
                Typeface = EmojiTypeface,
                StrokeWidth = 3
                // TextSkewX = 3
            };
for (int i = 0; i < Text.Length; i++)
            {
                ///偏移
                int xOffset = Random1.Next(-TextSize * 2 / 10, 1);///x偏移
                int yOffset = Random1.Next(-3, 3);///y偏移
                int angleOffset = Random1.Next(-15, 15);///角度偏移
                paint.Color = RandColoe();///随机颜色

                canvas.RotateDegrees(angleOffset, xPoint + xOffset, yPoint + yOffset);
                canvas.DrawText(Text[i].ToString(), xPoint + xOffset, yPoint + yOffset, paint);

                canvas.RotateDegrees(-angleOffset, xPoint + xOffset, yPoint + yOffset);
                // xPoint = xPoint + xOffset + TextSize;
                xPoint = xPoint + TextSize + xOffset;


            }

        }

        /// <summary>
        /// 混淆
        /// </summary>
        public void Confuse(uint Count)
        {
            // the the canvas and properties
            var canvas = Surface.Canvas;

            for (int i = 0; i < Count; i++)
            {
                int x = Random1.Next(0, canvas.DeviceClipBounds.Width);
                int y = Random1.Next(0, canvas.DeviceClipBounds.Height);
                int radius = Random1.Next(TextSize, TextSize * 2);

                var paint = new SKPaint
                {
                    Color = RandColoe(),///随机颜色
                    IsAntialias = true,
                    Style = SKPaintStyle.Stroke,
                    TextAlign = SKTextAlign.Left,
                    TextSize = TextSize,
                    TextEncoding = SKTextEncoding.Utf8,
                    Typeface = EmojiTypeface,
                    StrokeWidth = 1
                    // TextSkewX = 3
                };


                canvas.DrawCircle(x, y, radius, paint);

            }

        }

        /// <summary>
        /// 生成和背景具有区分的随机颜色
        /// </summary>
        /// <returns></returns>
        private SKColor RandColoe()
        {
            ///随机颜色,以背景色为基准,,取随机值,防止靠色
            ///0    128     256

            ///r值
            var r = Random1.Next(0, 255 - BoundsR.length);
            if (r > BoundsR.begin && r < BoundsR.end)
            {
                r = (r + BoundsR.length) % 256;
            }

            ///g值
            var g = (r + Random1.Next(50, 190)) % 256;

            if (g > BoundsG.begin && g < BoundsG.end)
            {
                g = (g + BoundsG.length) % 256;
            }

            ///b值
            var b = (g + Random1.Next(50, 190)) % 256;
            if (b > BoundsB.begin && b < BoundsB.end)
            {
                b = (b + BoundsB.length) % 256;
            }


            return new SKColor((byte)r, (byte)g, (byte)b);
        }

        /// <summary>
        /// 获取图片流,png格式
        /// </summary>
        /// <returns></returns>
        public byte[] GetImg()
        {
            MemoryStream stream;
            // save the file
            using (var image = Surface.Snapshot())
            using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
            {

                return data.ToArray();

            }
        }

        /// <summary>
        /// 一键生成验证图片
        /// </summary>
        /// <returns></returns>
        public byte[] CreateVerificationImage(string Text)
        {
            Clear();
            WriteText(Text);
            Confuse(4);
            var stream = GetImg();

            return stream;
        }


    }

public IActionResult img2()
        {
            ImageVerificationCode t1 = new ImageVerificationCode(135,40);

            t1.Clear();
            t1.WriteText("eB4FJ");
            t1.Confuse(5);
            var img= t1.GetImg();            

            return File(img, "image/Png");

        }
文档更新时间: 2023-09-03 18:03   作者:admin