.net 8 + SkiaSharp 实现 黑底,浅色字体 6 位字符,扭曲,数字+字母,粘连+干扰线的图形验证码。
先看效果:




实现代码如下:
using SkiaSharp; // 4.151.1 public class verify_code { private static readonly char[] character_table = { '1','2','3','4','5','6','7','8','9', 'A', 'C','D', 'F', 'H', 'J','K','L', 'N', 'P', 'R','S','T', 'U','V', 'X','Y','Z', }; private static readonly Random rnd = new Random(); private static readonly double[,] wave_matrix; private static readonly SKSizeI wave_matrix_size; private static readonly SKColor[] colors = { SKColors.Red, SKColors.LimeGreen, SKColors.DeepSkyBlue, SKColors.Yellow, SKColors.LightGray, SKColors.Orange }; static verify_code() { wave_matrix_size = new SKSizeI(600, 600); wave_matrix = new double[wave_matrix_size.Width, wave_matrix_size.Height]; for (int i = 0; i < wave_matrix_size.Width; i++) { for (int j = 0; j < wave_matrix_size.Height; j++) { wave_matrix[i, j] = (Math.Sin((i + j) * Math.PI / 130.0) + 1.0) / 2.0; } } } public static string NextCode(int length = 6) { return string.Concat(Enumerable.Range(0, length) .Select(_ => character_table[rnd.Next(character_table.Length)])); } public static SKBitmap NextImage(int length = 6, int width = 190, int height = 80) { string code = NextCode(length); return NextImage(code, width, height); } // ==================== 1. 修改 NextImage 方法(调用单条干扰线) ==================== public static SKBitmap NextImage(string code, int width = 240, int height = 100) { var color = colors[rnd.Next(colors.Length)]; using var font = new SKFont(SKTypeface.FromFamilyName("Arial", SKFontStyle.BoldItalic), 52); using var paint = new SKPaint { Color = color, IsAntialias = true, Style = SKPaintStyle.Fill, }; font.MeasureText(code, out var text_bounds); float spacing = -9.5f; // 粘连度 using var bitmap = new SKBitmap(width, height); using var canvas = new SKCanvas(bitmap); canvas.Clear(SKColors.Black); // ==================== 关键调整:整体靠左 ==================== float total_text_width = text_bounds.Width + spacing * (code.Length - 1); float x = (width - total_text_width) / 2f - 12; // ← 改为 -12(靠左),之前是 +5 float y = height * 0.68f; foreach (char c in code) { string ch = c.ToString(); canvas.DrawText(ch, x, y, SKTextAlign.Left, font, paint); x += font.MeasureText(ch) + spacing; } draw_noise_line(canvas, width, height, color); return apply_wave_distortion(bitmap); } // ==================== 2. 新增单条干扰线方法 ==================== private static void draw_noise_line(SKCanvas canvas, int width, int height, SKColor color) { using var paint = new SKPaint { Color = color.WithAlpha(200), StrokeWidth = 2.8f, Style = SKPaintStyle.Stroke, IsAntialias = true, }; var builder = new SKPathBuilder(); builder.MoveTo(rnd.Next(width / 4), rnd.Next(height)); // 绘制一条平滑的曲线 builder.CubicTo( rnd.Next(width * 2 / 5, width * 3 / 5), rnd.Next(height / 4, height * 3 / 4), rnd.Next(width * 2 / 5, width * 3 / 5), rnd.Next(height / 4, height * 3 / 4), rnd.Next(width * 3 / 4, width), rnd.Next(height)); canvas.DrawPath(builder.Detach(), paint); } private static SKBitmap apply_wave_distortion(SKBitmap source) { int w = source.Width; int h = source.Height; var result = new SKBitmap(w, h); using (var canvas = new SKCanvas(result)) canvas.Clear(SKColors.Black); int xi = rnd.Next(wave_matrix_size.Width - w); int yi = rnd.Next(wave_matrix_size.Width - w); int xj = rnd.Next(wave_matrix_size.Height - h); int yj = rnd.Next(wave_matrix_size.Height - h); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int nx = i - (int)(wave_matrix[xi + i, xj + j] * 12); int ny = j - (int)(wave_matrix[yi + i, yj + j] * 12); if (nx >= 0 && nx < w && ny >= 0 && ny < h) { result.SetPixel(i, j, source.GetPixel(nx, ny)); } } } return result; } }
控制中调用方法:
public IActionResult index() { // 1. 生成随机验证码文本 string code = verify_code.NextCode(); // 2. 存入 Session(后续验证用) HttpContext.Session.SetString("validate_code", code); // 3. 生成验证码图片(SKBitmap) using var bitmap = verify_code.NextImage(code, width: 240, height: 100); // 4. 编码为 PNG 并返回 using var image = SKImage.FromBitmap(bitmap); using var data = image.Encode(SKEncodedImageFormat.Png, 100); using var ms = new MemoryStream(); data.SaveTo(ms); return File(ms.ToArray(), "image/png"); }
验证方法:
string? validate_code = HttpContext.Session.GetString("validate_code"); HttpContext.Session.SetString("validate_code", ""); if (validate_code == null || string.IsNullOrEmpty(vercode)) { var fail = new { code = 200, msg = "请重新登录", success = false }; return Json(fail); } else { if (vercode.ToUpper() != validate_code.ToUpper()) { var fail = new { code = 200, msg = "图形验证码错误", success = false }; return Json(fail); } } // 下面是你自己的逻辑 ...

浙公网安备 33010602011771号