幸运蛋蛋pc开奖
这篇文章主要给大家介绍了关于.NET生成动态验证码的完整步骤,文中通过示例代码介绍的非常详细,对大家学习或者使用.NET具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

验证码是?#35745;?#19978;写上几个字,然后对这几个字做特殊处理,如扭曲、、修改文字位置,然后加入一些线条,或加入一些特殊效果,使这些在人类能正常识别的同时,机器却很难识别出来,以达到防爬虫、防机器人的效果。

验证码通常用于网站中,是防爬虫、防机器人侵入的好方法。以往.NET中创建验证码,通常会使用System.Drawing创建“正常”的验证码。

在前一往篇博客.NET中生成水印更好的方法中,提到了如何给?#35745;?#21152;水印。本文将基于上篇博客进一步探索,使用Direct2D创建验证码。

传统System.Drawing的做法

前置条件:引用System.Drawing,或者安装NuGet包:System.Drawing.Common

<PackageReference Include="System.Drawing.Common" Version="4.5.1" />

首?#21364;?#24314;一个有几个文字的?#35745;?#22522;本操作):

byte[] GetImage(int width, int height, string text)
{
 using (var bitmap = new Bitmap(width, height))
 using (var g = Graphics.FromImage(bitmap))
 {
  var r = new Random();

  g.Clear(ColorFromHsl(r.NextDouble(), 1.0f, 0.8f, 0xff));

  var brush = new SolidBrush(Color.Black);
  var fontSize = width / text.Length;
  var font = new Font(FontFamily.GenericSerif, fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
  for (var i = 0; i < text.Length; i++)
  {
   brush.Color = ColorFromHsl(r.NextDouble(), 1.0f, 0.3f, 0xff);
   float x = i * fontSize;
   float y = r.Next(0, height - fontSize);

   g.DrawString(text[i].ToString(), font, brush, x, y);
  }

  // 在这里面加入一些其它效果

  var ms = new MemoryStream();
  bitmap.Save(ms, ImageFormat.Png);
  return ms.ToArray();
 }
}

效果(Gif是由LINQPad生成多次截图而来,?#23548;?#20026;静态图):

然后再加入一些线条:

using (var pen = new Pen(brush, 3))
{
 for (var i = 0; i < 4; ++i)
 {
  pen.Color = ColorFromHsl(r.NextDouble(), 1.0f, 0.4f, 0xff);
  var p1 = new Point(r.Next(width), r.Next(height));
  var p2 = new Point(r.Next(width), r.Next(height));
  g.DrawLine(pen, p1, p2);
 }
}

效果(Gif是由LINQPad生成多次截图而来,?#23548;?#20026;静态图):

还能做什么?

很遗憾,还有很多可以做,即使是加入线条,机器依然能轻而易举地识别出来。

不过Edi.Wang在他的博客中也发布了一个生成验证码的NuGet包:Edi.Captcha,截止目前最新版是1.3.1:

<PackageReference Include="Edi.Captcha" Version="1.3.1" />

这个包基于System.Drawing,加入了扭曲效果,加入了一些随机的x坐标偏移,极大地增加了AI识别的?#35759;取?/p>

使用方式:

CaptchaResult result = CaptchaImageGenerator.GetImage(200, 100, "HELLO");

其中CaptchaResult的定义如下:

public class CaptchaResult
{
 public string CaptchaCode { get; set; }

 public byte[] CaptchaByteData { get; set; }

 public string CaptchBase64Data => Convert.ToBase64String(CaptchaByteData);

 public DateTime Timestamp { get; set; }
}

生成的效果如下(Gif是由LINQPad生成多次截图而来,?#23548;?#20026;静态图):

Direct2D

在前一篇博客中,已经有了Direct2D的相关简介。这里将不再介绍。

首?#21364;?#26368;简单的?#35745;?#19978;写文字开始:

byte[] SaveD2DBitmap(int width, int height, string text)
{
 using var wic = new WIC.ImagingFactory2();
 using var d2d = new D2D.Factory();
 using var wicBitmap = new WIC.Bitmap(wic, width, height, WIC.PixelFormat.Format32bppPBGRA, WIC.BitmapCreateCacheOption.CacheOnDemand);
 using var target = new D2D.WicRenderTarget(d2d, wicBitmap, new D2D.RenderTargetProperties());
 using var dwriteFactory = new SharpDX.DirectWrite.Factory();
 using var brush = new SolidColorBrush(target, Color.Yellow);
 
 var r = new Random();
 
 target.BeginDraw();
 target.Clear(ColorFromHsl(r.NextFloat(0, 1), 1.0f, 0.3f));
 var textFormat = new DWrite.TextFormat(dwriteFactory, "Times New Roman", 
  DWrite.FontWeight.Bold, 
  DWrite.FontStyle.Normal, 
  width / text.Length);
 for (int charIndex = 0; charIndex < text.Length; ++charIndex)
 {
  using var layout = new DWrite.TextLayout(dwriteFactory, text[charIndex].ToString(), textFormat, float.MaxValue, float.MaxValue);
  var layoutSize = new Vector2(layout.Metrics.Width, layout.Metrics.Height);
  using var b2 = new LinearGradientBrush(target, new D2D.LinearGradientBrushProperties
  {
   StartPoint = Vector2.Zero, 
   EndPoint = layoutSize, 
  }, new GradientStopCollection(target, new[]
  {
   new GradientStop{ Position = 0.0f, Color = ColorFromHsl(r.NextFloat(0, 1), 1.0f, 0.8f) },
   new GradientStop{ Position = 1.0f, Color = ColorFromHsl(r.NextFloat(0, 1), 1.0f, 0.8f) },
  }));

  var position = new Vector2(charIndex * width / text.Length, r.NextFloat(0, height - layout.Metrics.Height));
  target.Transform = 
   Matrix3x2.Translation(-layoutSize / 2) * 
   // 文字和扭曲效果,取消注释以下两行代码
   // Matrix3x2.Skew(r.NextFloat(0, 0.5f), r.NextFloat(0, 0.5f)) *
   // Matrix3x2.Rotation(r.NextFloat(0, MathF.PI * 2)) * 
   Matrix3x2.Translation(position + layoutSize / 2);
  target.DrawTextLayout(Vector2.Zero, layout, b2);
 }
 // 其它效果在这里插入

 target.EndDraw();

 using (var encoder = new WIC.BitmapEncoder(wic, WIC.ContainerFormatGuids.Png))
 using (var ms = new MemoryStream())
 {
  encoder.Initialize(ms);
  using (var frame = new WIC.BitmapFrameEncode(encoder))
  {
   frame.Initialize();
   frame.SetSize(wicBitmap.Size.Width, wicBitmap.Size.Height);

   var pixelFormat = wicBitmap.PixelFormat;
   frame.SetPixelFormat(ref pixelFormat);
   frame.WriteSource(wicBitmap);

   frame.Commit();
  }

  encoder.Commit();
  return ms.ToArray();
 }
}

使用方式:

byte[] captchaBytes = SaveD2DBitmap(200, 100, "Hello");

效果(Gif是由LINQPad生成多次截图而来,?#23548;?#20026;静态图):

可以注意到,Direct2D生成的文字没有System.Drawing那样的锯齿。

如果取消里面的两行注释,可以得到更加扭曲和的效果(Gif是由LINQPad生成多次截图而来,?#23548;?#20026;静态图):

然后加入线条:

for (var i = 0; i < 4; ++i)
{
 target.Transform = Matrix3x2.Identity;
 brush.Color = ColorFromHsl(r.NextFloat(0,1), 1.0f, 0.3f);
 target.DrawLine(
  r.NextVector2(Vector2.Zero, new Vector2(width, height)),
  r.NextVector2(Vector2.Zero, new Vector2(width, height)),
  brush, 3.0f);
}

效果(Gif是由LINQPad生成多次截图而来,?#23548;?#20026;静态图):

Direct2D的骚操作

Direct2D中内置了许多特效,如阴影(Shadow)等,这里我们需要用到的是位移特效(Displacement)和水流特效(Turbulence),为了实现特效,需要加入一个Bitmap层,整体代码如下:

byte[] SaveD2DBitmap(int width, int height, string text)
{
 using var wic = new WIC.ImagingFactory2();
 using var d2d = new D2D.Factory();
 using var wicBitmap = new WIC.Bitmap(wic, width, height, WIC.PixelFormat.Format32bppPBGRA, WIC.BitmapCreateCacheOption.CacheOnDemand);
 using var target = new D2D.WicRenderTarget(d2d, wicBitmap, new D2D.RenderTargetProperties());
 using var dwriteFactory = new SharpDX.DirectWrite.Factory();
 using var brush = new D2D.SolidColorBrush(target, Color.Yellow);
 using var encoder = new WIC.PngBitmapEncoder(wic); // PngBitmapEncoder
 
 using var ms = new MemoryStream();
 using var dc = target.QueryInterface<D2D.DeviceContext>();
 using var bmpLayer = new D2D.Bitmap1(dc, target.PixelSize,
  new D2D.BitmapProperties1(new D2D.PixelFormat(SharpDX.DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Premultiplied),
  d2d.DesktopDpi.Width, d2d.DesktopDpi.Height,
  D2D.BitmapOptions.Target));

 var r = new Random();
 encoder.Initialize(ms);

 D2D.Image oldTarget = dc.Target;
 {
  dc.Target = bmpLayer;
  dc.BeginDraw();
  var textFormat = new DWrite.TextFormat(dwriteFactory, "Times New Roman",
   DWrite.FontWeight.Bold,
   DWrite.FontStyle.Normal,
   width / text.Length);
  for (int charIndex = 0; charIndex < text.Length; ++charIndex)
  {
   using var layout = new DWrite.TextLayout(dwriteFactory, text[charIndex].ToString(), textFormat, float.MaxValue, float.MaxValue);
   var layoutSize = new Vector2(layout.Metrics.Width, layout.Metrics.Height);
   using var b2 = new D2D.LinearGradientBrush(dc, new D2D.LinearGradientBrushProperties
   {
    StartPoint = Vector2.Zero,
    EndPoint = layoutSize,
   }, new D2D.GradientStopCollection(dc, new[]
   {
    new D2D.GradientStop{ Position = 0.0f, Color = ColorFromHsl(r.NextFloat(0, 1), 1.0f, 0.8f) },
    new D2D.GradientStop{ Position = 1.0f, Color = ColorFromHsl(r.NextFloat(0, 1), 1.0f, 0.8f) },
   }));

   var position = new Vector2(charIndex * width / text.Length, r.NextFloat(0, height - layout.Metrics.Height));
   dc.Transform =
    Matrix3x2.Translation(-layoutSize / 2) *
    Matrix3x2.Skew(r.NextFloat(0, 0.5f), r.NextFloat(0, 0.5f)) *
    //Matrix3x2.Rotation(r.NextFloat(0, MathF.PI * 2)) *
    Matrix3x2.Translation(position + layoutSize / 2);
   dc.DrawTextLayout(Vector2.Zero, layout, b2);
  }
  for (var i = 0; i < 4; ++i)
  {
   target.Transform = Matrix3x2.Identity;
   brush.Color = ColorFromHsl(r.NextFloat(0, 1), 1.0f, 0.3f);
   target.DrawLine(
    r.NextVector2(Vector2.Zero, new Vector2(width, height)),
    r.NextVector2(Vector2.Zero, new Vector2(width, height)),
    brush, 3.0f);
  }
  target.EndDraw();
 }
 
 Color background = ColorFromHsl(r.NextFloat(0, 1), 1.0f, 0.3f);
 // for (var frameId = -10; frameId < 10; ++frameId)
 {
  dc.Target = null;
  using var displacement = new D2D.Effects.DisplacementMap(dc);
  displacement.SetInput(0, bmpLayer, true);
  displacement.Scale = 100.0f; // Math.Abs(frameId) * 10.0f;
  
  var turbulence = new D2D.Effects.Turbulence(dc);
  displacement.SetInputEffect(1, turbulence);

  dc.Target = oldTarget;
  dc.BeginDraw();
  dc.Clear(background);
  dc.DrawImage(displacement);
  dc.EndDraw();

  using (var frame = new WIC.BitmapFrameEncode(encoder))
  {
   frame.Initialize();
   frame.SetSize(wicBitmap.Size.Width, wicBitmap.Size.Height);

   var pixelFormat = wicBitmap.PixelFormat;
   frame.SetPixelFormat(ref pixelFormat);
   frame.WriteSource(wicBitmap);

   frame.Commit();
  }
 }

 encoder.Commit();
 return ms.ToArray();
}

注意此代码使用了using var语句,是C# 8.0的using declaration功能,可以用using (var )语句代替。

效果如下(Gif是由LINQPad生成多次截图而来,?#23548;?#20026;静态图):

在此基础上,(?#34892;?code>Direct2D/WIC)经过较小的改动,即可生成一个动态的Gif?#35745;?/p>

只要略微修改以上代码:

    PngBitmapEncoder改成GifBitmapEncoder* 然后将下面的for循环取消注释 将displacement.Scale = 100.0f;改成displacement.Scale = Math.Abs(frameId) * 10.0f;

即可看到以下效果(直接生成,非截图):



结语

最终的代码生成效果,可以从这里下载,用LINQPad 6打开。

本文使用的是SharpDX,是C#到DirectX的转换层。一个坏消息是,上图中使用的SharpDX已经停止维护了,但目前还没找到可以用于替换的库(可能由于它太好用了)。

以前我经常将Direct2D用于游戏,但最近越来越多的时候Direct2D已经用于解决?#23548;?#38382;题。由于Direct2D的高颜值、高性能,?#23548;?#19978;,Direct2D已经无处不在,浏览器/Word/Excel等日常软件都是深度集成Direct2D的应用。相信Direct2D可以用于更多的中。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对爱安网的支持。

最新资讯
小罐茶杜国楹谈“科学创业”:选择要顺势而为

小罐茶杜国楹谈“科学

第十二届创业家年会暨产?#23548;?#36895;大会上,小罐茶创始人、董
小鹏P7启动预售:价格区间27-37万元 续航超650公里

小鹏P7启动预售:价格区

小鹏汽车表示,P7智能音乐座舱不是功能和配置的简单叠加
威马EX6 Plus正式上市 综合补贴后售价23.99万元

威马EX6 Plus正式上市

威马汽车旗下长续航智能5座SUV——威马EX6 Plus于广州
邬贺铨:工业互联网牵引5G 5G赋能数字转型

邬贺铨:工业互联网牵引

邬贺铨在演讲中表示,1G-4G主要面向消费,5G则扩展到智慧
中国工程院公布2019年院士增选结果:阿里王坚等当选

中国工程院公布2019年

2019年,中国工程院开展了第14次院士增选和第13次外籍院
杨元庆回应产品安全:在任何市场都会符合法律要求

杨元庆回应产品安全:在

联想集团董事长兼CEO杨元庆22日在创新经济论?#25104;?#34920;示,
最新文章
.NET生成水印更好的方法实例代码

.NET生成水印更好的方

这篇文章主要给大家介绍了关于.NET中生成水印更好的方
ASP.NET如何自定义项目模板详解

ASP.NET如何自定义项

这篇文章主要给大家介绍了关于ASP.NET如何自定义项目
.NET生成动态验证码的完整步骤

.NET生成动态验证码的

这篇文章主要给大家介绍了关于.NET生成动态验证码的完
ASP.NET Core MVC学习之视图组件(View Component)

ASP.NET Core MVC学习

这篇文章主要给大家介绍了关于ASP.NET Core MVC学习之
你应该知道的.NET错误与异常处理机制

你应该知道的.NET错误

这篇文章主要给大家介绍了关于.NET错误与异常处理机制
WCF中使用nettcp协议进行通讯的方法

WCF中使用nettcp协议

这篇文章主要给大家介绍了关于WCF中使用nettcp协议进
幸运蛋蛋pc开奖 天津十一选五推荐号 香港马会经一码中特 江苏十一选五的开奖 天天乐棋牌斗地主 河北11选5选号软件 西安娱乐场所 天津11选5遗漏号top 玄机生肖藏宝图 3d杀组选复式投注技巧 彩票开奖结果查询