using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Security.Cryptography; using System.Text; namespace MilliSim.Services.Infrastructure; public interface ICaptchaService { (string CaptchaId, string Svg) GenerateCaptcha(); bool ValidateCaptcha(string captchaId, string code); } public class CaptchaService : ICaptchaService { private static readonly Dictionary _store = new(); private static readonly object _lock = new(); private static readonly char[] _chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789".ToCharArray(); public (string CaptchaId, string Svg) GenerateCaptcha() { var code = new string(Enumerable.Range(0, 4).Select(_ => _chars[RandomNumberGenerator.GetInt32(_chars.Length)]).ToArray()); var captchaId = Guid.NewGuid().ToString("N"); lock (_lock) { _store[captchaId] = (code, DateTime.Now.AddMinutes(5)); // 清理过期 var expired = _store.Where(x => x.Value.Expire < DateTime.Now).Select(x => x.Key).ToList(); foreach (var key in expired) _store.Remove(key); } var svg = DrawSvg(code); return (captchaId, svg); } public bool ValidateCaptcha(string captchaId, string code) { if (string.IsNullOrEmpty(captchaId) || string.IsNullOrEmpty(code)) return false; lock (_lock) { if (!_store.TryGetValue(captchaId, out var item)) return false; _store.Remove(captchaId); if (item.Expire < DateTime.Now) return false; return string.Equals(item.Code, code, StringComparison.OrdinalIgnoreCase); } } private static string DrawSvg(string code) { var random = new Random(); var width = 120; var height = 40; var sb = new StringBuilder(); sb.Append($""); // 背景 sb.Append($""); // 干扰线 for (var i = 0; i < 4; i++) { var x1 = random.Next(width); var y1 = random.Next(height); var x2 = random.Next(width); var y2 = random.Next(height); var color = GetRandomColor(random); sb.Append($""); } // 干扰点 for (var i = 0; i < 20; i++) { var x = random.Next(width); var y = random.Next(height); var color = GetRandomColor(random); sb.Append($""); } // 文字 var colors = new[] { "#0958D9", "#1677FF", "#52C41A", "#722ED1", "#FA8C16" }; for (var i = 0; i < code.Length; i++) { var x = 15 + i * 25; var y = 28 + random.Next(-3, 4); var angle = random.Next(-15, 16); var color = colors[random.Next(colors.Length)]; var fontSize = random.Next(22, 28); sb.Append($"{code[i]}"); } sb.Append(""); return sb.ToString(); } private static string GetRandomColor(Random random) { var colors = new[] { "#0958D9", "#1677FF", "#52C41A", "#722ED1", "#FA8C16", "#13C2C2" }; return colors[random.Next(colors.Length)]; } }