Browse Source

新增模板背景图和滑块模型图
优化滑块拼图生成

zhontai 2 years ago
parent
commit
7fa7fdce39
31 changed files with 101 additions and 65 deletions
  1. 33 15
      src/hosts/ZhonTai.Host/ZhonTai.Host.csproj
  2. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/1.jpg
  3. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/2.jpg
  4. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/3.jpg
  5. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/1.jpg
  6. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/2.jpg
  7. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/3.jpg
  8. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/4.jpg
  9. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/5.jpg
  10. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/1.png
  11. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/1/dark.png
  12. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/1/transparent.png
  13. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/2.png
  14. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/2/dark.png
  15. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/2/transparent.png
  16. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/3.png
  17. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/3/dark.png
  18. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/3/transparent.png
  19. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/4.png
  20. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/4/dark.png
  21. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/4/transparent.png
  22. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/5.png
  23. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/5/dark.png
  24. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/5/transparent.png
  25. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/6.png
  26. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/6/dark.png
  27. BIN
      src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/6/transparent.png
  28. 47 39
      src/platform/ZhonTai.Admin/Tools/Captcha/SlideJigsawCaptchaTool.cs
  29. 8 10
      src/platform/ZhonTai.Admin/Tools/Captcha/VerifyCodeHelper.cs
  30. 2 1
      src/platform/ZhonTai.Admin/ZhonTai.Admin.xml
  31. 11 0
      src/tests/ZhonTai.Tests/Services/ApiServiceTest.cs

+ 33 - 15
src/hosts/ZhonTai.Host/ZhonTai.Host.csproj

@@ -14,32 +14,50 @@
 	</ItemGroup>
 
 	<ItemGroup>
-		<Content Update="wwwroot\captcha\jigsaw\1.jpg">
+		<Content Update="wwwroot\captcha\jigsaw\backgrounds\1.jpg">
 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</Content>
-		<Content Update="wwwroot\captcha\jigsaw\2.jpg">
+		<Content Update="wwwroot\captcha\jigsaw\backgrounds\2.jpg">
 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</Content>
-		<Content Update="wwwroot\captcha\jigsaw\3.jpg">
+		<Content Update="wwwroot\captcha\jigsaw\backgrounds\3.jpg">
 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</Content>
-		<Content Update="wwwroot\captcha\jigsaw\templates\1.png">
-			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		<Content Update="wwwroot\captcha\jigsaw\templates\1\dark.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</Content>
-		<Content Update="wwwroot\captcha\jigsaw\templates\2.png">
-			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		<Content Update="wwwroot\captcha\jigsaw\templates\1\transparent.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</Content>
-		<Content Update="wwwroot\captcha\jigsaw\templates\3.png">
-			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		<Content Update="wwwroot\captcha\jigsaw\templates\2\dark.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</Content>
-		<Content Update="wwwroot\captcha\jigsaw\templates\4.png">
-			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		<Content Update="wwwroot\captcha\jigsaw\templates\2\transparent.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</Content>
-		<Content Update="wwwroot\captcha\jigsaw\templates\5.png">
-			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		<Content Update="wwwroot\captcha\jigsaw\templates\3\dark.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</Content>
-		<Content Update="wwwroot\captcha\jigsaw\templates\6.png">
-			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		<Content Update="wwwroot\captcha\jigsaw\templates\3\transparent.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		</Content>
+		<Content Update="wwwroot\captcha\jigsaw\templates\4\dark.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		</Content>
+		<Content Update="wwwroot\captcha\jigsaw\templates\4\transparent.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		</Content>
+		<Content Update="wwwroot\captcha\jigsaw\templates\5\dark.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		</Content>
+		<Content Update="wwwroot\captcha\jigsaw\templates\5\transparent.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		</Content>
+		<Content Update="wwwroot\captcha\jigsaw\templates\6\dark.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		</Content>
+		<Content Update="wwwroot\captcha\jigsaw\templates\6\transparent.png">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</Content>
 		<Content Update="wwwroot\swagger\mini-profiler.js">
 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/1.jpg


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/2.jpg


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/3.jpg


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/1.jpg


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/2.jpg


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/3.jpg


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/4.jpg


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/backgrounds/5.jpg


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/1.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/1/dark.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/1/transparent.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/2.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/2/dark.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/2/transparent.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/3.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/3/dark.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/3/transparent.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/4.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/4/dark.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/4/transparent.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/5.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/5/dark.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/5/transparent.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/6.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/6/dark.png


BIN
src/hosts/ZhonTai.Host/wwwroot/captcha/jigsaw/templates/6/transparent.png


+ 47 - 39
src/platform/ZhonTai.Admin/Tools/Captcha/SlideJigsawCaptchaTool.cs

@@ -12,12 +12,6 @@ using SixLabors.ImageSharp.PixelFormats;
 using ZhonTai.Admin.Core.Attributes;
 using ZhonTai.Admin.Tools.Cache;
 
-/*
-Linux下Ubuntu如果报Gdip错误,需要按照以下步骤操作
-1. apt-get install libgdiplus
-2. cd /usr/lib
-3. ln -s libgdiplus.so gdiplus.dll
-*/
 namespace ZhonTai.Admin.Tools.Captcha
 {
     /// <summary>
@@ -28,6 +22,8 @@ namespace ZhonTai.Admin.Tools.Captcha
     {
         private readonly ICacheTool _cache;
 
+        private readonly Random _random = new();
+
         public SlideJigsawCaptchaTool(ICacheTool cache)
         {
             _cache = cache;
@@ -39,9 +35,9 @@ namespace ZhonTai.Admin.Tools.Captcha
         /// <param name="startNum"></param>
         /// <param name="endNum"></param>
         /// <returns></returns>
-		private static int GetRandomInt(int startNum, int endNum)
+		private int GetRandomInt(int startNum, int endNum)
         {
-            return (endNum > startNum ? new Random().Next(endNum - startNum) : 0) + startNum;
+            return (endNum > startNum ? _random.Next(endNum - startNum) : 0) + startNum;
         }
 
         /// <summary>
@@ -52,9 +48,8 @@ namespace ZhonTai.Admin.Tools.Captcha
         /// <param name="templateWidth"></param>
         /// <param name="templateHeight"></param>
         /// <returns></returns>
-        private static PointModel GeneratePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight)
+        private PointModel GeneratePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight)
         {
-            var random = new Random();
             int widthDifference = originalWidth - templateWidth;
             int heightDifference = originalHeight - templateHeight;
             int x;
@@ -64,7 +59,7 @@ namespace ZhonTai.Admin.Tools.Captcha
             }
             else
             {
-                x = random.Next(originalWidth - templateWidth - 100) + 100;
+                x = _random.Next(originalWidth - templateWidth - 100) + 100;
             }
 
             int y;
@@ -74,7 +69,7 @@ namespace ZhonTai.Admin.Tools.Captcha
             }
             else
             {
-                y = random.Next(originalHeight - templateHeight - 5) + 5;
+                y = _random.Next(originalHeight - templateHeight - 5) + 5;
             }
 
             return new PointModel(x, y);
@@ -90,7 +85,7 @@ namespace ZhonTai.Admin.Tools.Captcha
         /// <param name="blockX"></param>
         /// <param name="blockY"></param>
         /// <returns></returns>
-        private static PointModel GenerateInterferencePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight, int blockX, int blockY)
+        private PointModel GenerateInterferencePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight, int blockX, int blockY)
         {
             int x;
             if (originalWidth - blockX - 5 > templateWidth * 2)
@@ -119,13 +114,13 @@ namespace ZhonTai.Admin.Tools.Captcha
             return new PointModel(x, y);
         }
 
-        private static ComplexPolygon CalcBlockShape(Image<Rgba32> holeTemplateImage)
+        private static ComplexPolygon CalcBlockShape(Image<Rgba32> templateDarkImage)
         {
             int temp = 0;
             var pathList = new List<IPath>();
-            holeTemplateImage.ProcessPixelRows(accessor =>
+            templateDarkImage.ProcessPixelRows(accessor =>
             {
-                for (int y = 0; y < holeTemplateImage.Height; y++)
+                for (int y = 0; y < templateDarkImage.Height; y++)
                 {
                     var rowSpan = accessor.GetRowSpan(y);
                     for (int x = 0; x < rowSpan.Length; x++)
@@ -156,7 +151,8 @@ namespace ZhonTai.Admin.Tools.Captcha
         /// <summary>
         /// 获得验证数据
         /// </summary>
-        /// <returns>JObject</returns>
+        /// <param name="captchaKey"></param>
+        /// <returns></returns>
         public async Task<CaptchaOutput> GetAsync(string captchaKey)
         {
             //获取网络图片
@@ -165,45 +161,57 @@ namespace ZhonTai.Admin.Tools.Captcha
             //client.Dispose();
 
             //底图
-            using var baseImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\{new Random().Next(1, 4)}.jpg".ToPath());
-            //模板图
-            using var templateImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\templates\{new Random().Next(1, 7)}.png".ToPath());
+            using var baseImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\backgrounds\{_random.Next(1, 6)}.jpg".ToPath());
+            var randomTemplate = _random.Next(1, 7);
+            //深色模板图
+            using var darkTemplateImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\templates\{randomTemplate}\dark.png".ToPath());
+            //透明模板图
+            using var transparentTemplateImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\templates\{randomTemplate}\transparent.png".ToPath());
 
             int baseWidth = baseImage.Width;
             int baseHeight = baseImage.Height;
-            int templateWidth = templateImage.Width;
-            int templateHeight = templateImage.Height;
+            int blockWidth = 50;
+            int blockHeight = 50;
 
-            //拼图
-            using var blockImage = new Image<Rgba32>(templateWidth, templateHeight);
-            //滑块拼图
-            using var sliderBlockImage = new Image<Rgba32>(templateWidth, baseHeight);
+            //调整模板图大小
+            darkTemplateImage.Mutate(x =>
+            {
+                x.Resize(blockWidth, blockHeight);
+            });
+            transparentTemplateImage.Mutate(x =>
+            {
+                x.Resize(blockWidth, blockHeight);
+            });
+
+            //新建拼图
+            using var blockImage = new Image<Rgba32>(blockWidth, blockHeight);
+            //新建滑块拼图
+            using var sliderBlockImage = new Image<Rgba32>(blockWidth, baseHeight);
 
             //随机生成拼图坐标
-            PointModel blockPoint = GeneratePoint(baseWidth, baseHeight, templateWidth, templateHeight);
+            PointModel blockPoint = GeneratePoint(baseWidth, baseHeight, blockWidth, blockHeight);
+
+            //根据深色模板图计算轮廓形状
+            var blockShape = CalcBlockShape(darkTemplateImage);
 
-            //根据模板图计算轮廓
-            var blockShape = CalcBlockShape(templateImage);
             //生成拼图
             blockImage.Mutate(x =>
             {
                 x.Clip(blockShape, p => p.DrawImage(baseImage, new Point(-blockPoint.X, -blockPoint.Y), 1));
             });
-            //叠加拼图
-            //blockImage.Mutate(x => x.DrawImage(templateImage, new Point(0, 0), 1));
+            //拼图叠加透明模板图层
+            blockImage.Mutate(x => x.DrawImage(transparentTemplateImage, new Point(0, 0), 1));
 
             //生成滑块拼图
             sliderBlockImage.Mutate(x => x.DrawImage(blockImage, new Point(0, blockPoint.Y), 1));
 
-            //生成拼图底图
-            baseImage.Mutate(x => x.DrawImage(templateImage, new Point(blockPoint.X, blockPoint.Y), (float)0.5));
-
+            //底图叠加深色模板图
+            baseImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(blockPoint.X, blockPoint.Y), 0.5f));
             //生成干扰图坐标
-            PointModel interferencePoint = GenerateInterferencePoint(baseWidth, baseHeight, templateWidth, templateHeight, blockPoint.X, blockPoint.Y);
-
-            //生成干扰图底图
-            baseImage.Mutate(x => x.DrawImage(templateImage, new Point(interferencePoint.X, interferencePoint.Y), (float)0.5));
-
+            PointModel interferencePoint = GenerateInterferencePoint(baseWidth, baseHeight, blockWidth, blockHeight, blockPoint.X, blockPoint.Y);
+            //底图叠加深色干扰模板图
+            baseImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(interferencePoint.X, interferencePoint.Y), 0.5f));
+            
             var token = Guid.NewGuid().ToString();
             var captchaData = new CaptchaOutput
             {

+ 8 - 10
src/platform/ZhonTai.Admin/Tools/Captcha/VerifyCodeHelper.cs

@@ -22,7 +22,7 @@ namespace ZhonTai.Admin.Tools.Captcha
             _appConfig = appConfig;
         }
 
-        private string GenerateRandom(int length)
+        private static string GenerateRandom(int length)
         {
             var chars = new StringBuilder();
             //验证码的字符集,去掉了一些容易混淆的字符
@@ -38,8 +38,8 @@ namespace ZhonTai.Admin.Tools.Captcha
 
         public byte[] Draw(out string code, int length = 4)
         {
-            int codeW = 110;
-            int codeH = 36;
+            int width = 110;
+            int height = 36;
             int fontSize = 22;
 
             //颜色列表,用于验证码、噪线、噪点
@@ -50,7 +50,7 @@ namespace ZhonTai.Admin.Tools.Captcha
             var chars = GenerateRandom(length);
             code = chars;
 
-            using var img = new Image<Rgba32>(codeW, codeH, Color.White);
+            using var img = new Image<Rgba32>(width, height, Color.White);
 
             img.Mutate(ctx =>
             {
@@ -58,12 +58,10 @@ namespace ZhonTai.Admin.Tools.Captcha
                 //画噪线
                 for (int i = 0; i < 1; i++)
                 {
-                    int x1 = rnd.Next(codeW);
-                    int y1 = rnd.Next(codeH);
-                    int x2 = rnd.Next(codeW);
-                    int y2 = rnd.Next(codeH);
-                    int ctrlx1 = rnd.Next(codeW / 4, codeW / 4 * 3), ctrly1 = rnd.Next(5, codeH - 5);
-                    int ctrlx2 = rnd.Next(codeW / 4, codeW / 4 * 3), ctrly2 = rnd.Next(5, codeH - 5);
+                    int x1 = rnd.Next(width), y1 = rnd.Next(height);
+                    int x2 = rnd.Next(width), y2 = rnd.Next(height);
+                    int ctrlx1 = rnd.Next(width / 4, width / 4 * 3), ctrly1 = rnd.Next(5, height - 5);
+                    int ctrlx2 = rnd.Next(width / 4, width / 4 * 3), ctrly2 = rnd.Next(5, height - 5);
                     Color color = colors[rnd.Next(colors.Length)];
                     ctx.DrawBeziers(new DrawingOptions
                     {

+ 2 - 1
src/platform/ZhonTai.Admin/ZhonTai.Admin.xml

@@ -6275,7 +6275,8 @@
             <summary>
             获得验证数据
             </summary>
-            <returns>JObject</returns>
+            <param name="captchaKey"></param>
+            <returns></returns>
         </member>
         <member name="M:ZhonTai.Admin.Tools.Captcha.SlideJigsawCaptchaTool.CheckAsync(ZhonTai.Admin.Tools.Captcha.CaptchaInput)">
             <summary>

+ 11 - 0
src/tests/ZhonTai.Tests/Services/ApiServiceTest.cs

@@ -1,5 +1,6 @@
 using Xunit;
 using ZhonTai.Admin.Services.Api;
+using ZhonTai.Admin.Tools.Captcha;
 
 namespace ZhonTai.Tests.Services
 {
@@ -7,9 +8,19 @@ namespace ZhonTai.Tests.Services
     {
         private readonly IApiService _apiService;
 
+        private readonly ICaptchaTool _captchaTool;
+
         public ApiServiceTest()
         {
             _apiService = GetService<IApiService>();
+
+            _captchaTool = GetService<ICaptchaTool>(); ;
+        }
+
+        [Fact]
+        public async void SlideJigsawTest()
+        {
+            var data = await _captchaTool.GetAsync("admin:captcha:{0}");
         }
 
         [Fact]