100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > ASP.NET Core 生成验证码

ASP.NET Core 生成验证码

时间:2024-01-31 10:48:42

相关推荐

ASP.NET Core 生成验证码

点击蓝字

关注我

使用验证码保护网站免受垃圾信息的选择有很多,比如Google ReCaptcha和。这两者都可以整合到 Core应用中去。然而,如果你出于某些原因,仍然希望自己写验证码,例如你下网站需要在中国大陆使用,那么本文会教你如何在最新版的 Core中生成和使用验证码。

我所使用的方法是在微软样例代码库https://code./How-to-make-and-use-d0d1752a 的基础之上做了一些修改,以运行于.NET Core 2.x上,并也有一些改进。

验证码是如何工作的

一个简单的验证码原理是生成一串随机字符(数字或字母),将字符串保存到Session中,同时生成一张图片用来显示在网页上。当用户提交内容到服务器的时,服务器检查用户输入的验证码是否与Session中的一致,以此判断验证码是否正确。流程如下图:

这个样例是我下一版本博客中的验证码:

在 Core 2.1 中实现验证码

在了解验证码工作流程之后,我们来看看如何实现。

1

准备工作

首先,你需要在工程属性的DebugRelease模式里都勾选"Allow unsafe code"。

我们需要使用System.Drawing.Imaging命名空间里的类型,所以我们也需要安装一个NuGet包:

Install-Package mon -Version 4.5.1

因为验证码依赖Session存储,所以我们也需要在 Core中启用Session支持。在Startup.cs里加入:

public void ConfigureServices(IServiceCollection services)

{

// other code...

// add session support

services.AddSession(options =>

{

options.IdleTimeout = TimeSpan.FromMinutes(20);

options.Cookie.HttpOnly = true;

});

// other code...

}

还有这里:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)

{

// other code...

// add session support

app.UseSession();

// other code...

}

注意:Session依赖Cookie才能工作,所以请确保用户首先接受GDPR cookie策略,这是 Core 2.1默认模板里添加的。

2

生成验证码

新建一个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; }

}

以及一个Captcha类来生成并验证验证码

public static class Captcha

{

const string Letters = "2346789ABCDEFGHJKLMNPRTUVWXYZ";

public static string GenerateCaptchaCode()

{

Random rand = new Random();

int maxRand = Letters.Length - 1;

StringBuilder sb = new StringBuilder();

for (int i = 0; i < 4; i++)

{

int index = rand.Next(maxRand);

sb.Append(Letters[index]);

}

return sb.ToString();

}

public static bool ValidateCaptchaCode(string userInputCaptcha, HttpContext context)

{

var isValid = userInputCaptcha == context.Session.GetString("CaptchaCode");

context.Session.Remove("CaptchaCode");

return isValid;

}

public static CaptchaResult GenerateCaptchaImage(int width, int height, string captchaCode)

{

using (Bitmap baseMap = new Bitmap(width, height))

using (Graphics graph = Graphics.FromImage(baseMap))

{

Random rand = new Random();

graph.Clear(GetRandomLightColor());

DrawCaptchaCode();

DrawDisorderLine();

AdjustRippleEffect();

MemoryStream ms = new MemoryStream();

baseMap.Save(ms, ImageFormat.Png);

return new CaptchaResult { CaptchaCode = captchaCode, CaptchaByteData = ms.ToArray(), Timestamp = DateTime.Now };

int GetFontSize(int imageWidth, int captchCodeCount)

{

var averageSize = imageWidth / captchCodeCount;

return Convert.ToInt32(averageSize);

}

Color GetRandomDeepColor()

{

int redlow = 160, greenLow = 100, blueLow = 160;

return Color.FromArgb(rand.Next(redlow), rand.Next(greenLow), rand.Next(blueLow));

}

Color GetRandomLightColor()

{

int low = 180, high = 255;

int nRend = rand.Next(high) % (high - low) + low;

int nGreen = rand.Next(high) % (high - low) + low;

int nBlue = rand.Next(high) % (high - low) + low;

return Color.FromArgb(nRend, nGreen, nBlue);

}

void DrawCaptchaCode()

{

SolidBrush fontBrush = new SolidBrush(Color.Black);

int fontSize = GetFontSize(width, captchaCode.Length);

Font font = new Font(FontFamily.GenericSerif, fontSize, FontStyle.Bold, GraphicsUnit.Pixel);

for (int i = 0; i < captchaCode.Length; i++)

{

fontBrush.Color = GetRandomDeepColor();

int shiftPx = fontSize / 6;

float x = i * fontSize + rand.Next(-shiftPx, shiftPx) + rand.Next(-shiftPx, shiftPx);

int maxY = height - fontSize;

if (maxY < 0) maxY = 0;

float y = rand.Next(0, maxY);

graph.DrawString(captchaCode[i].ToString(), font, fontBrush, x, y);

}

}

void DrawDisorderLine()

{

Pen linePen = new Pen(new SolidBrush(Color.Black), 3);

for (int i = 0; i < rand.Next(3, 5); i++)

{

linePen.Color = GetRandomDeepColor();

Point startPoint = new Point(rand.Next(0, width), rand.Next(0, height));

Point endPoint = new Point(rand.Next(0, width), rand.Next(0, height));

graph.DrawLine(linePen, startPoint, endPoint);

//Point bezierPoint1 = new Point(rand.Next(0, width), rand.Next(0, height));

//Point bezierPoint2 = new Point(rand.Next(0, width), rand.Next(0, height));

//graph.DrawBezier(linePen, startPoint, bezierPoint1, bezierPoint2, endPoint);

}

}

void AdjustRippleEffect()

{

short nWave = 6;

int nWidth = baseMap.Width;

int nHeight = baseMap.Height;

Point[,] pt = new Point[nWidth, nHeight];

for (int x = 0; x < nWidth; ++x)

{

for (int y = 0; y < nHeight; ++y)

{

var xo = nWave * Math.Sin(2.0 * 3.1415 * y / 128.0);

var yo = nWave * Math.Cos(2.0 * 3.1415 * x / 128.0);

var newX = x + xo;

var newY = y + yo;

if (newX > 0 && newX < nWidth)

{

pt[x, y].X = (int)newX;

}

else

{

pt[x, y].X = 0;

}

if (newY > 0 && newY < nHeight)

{

pt[x, y].Y = (int)newY;

}

else

{

pt[x, y].Y = 0;

}

}

}

Bitmap bSrc = (Bitmap)baseMap.Clone();

BitmapData bitmapData = baseMap.LockBits(new Rectangle(0, 0, baseMap.Width, baseMap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

int scanline = bitmapData.Stride;

IntPtr scan0 = bitmapData.Scan0;

IntPtr srcScan0 = bmSrc.Scan0;

unsafe

{

byte* p = (byte*)(void*)scan0;

byte* pSrc = (byte*)(void*)srcScan0;

int nOffset = bitmapData.Stride - baseMap.Width * 3;

for (int y = 0; y < nHeight; ++y)

{

for (int x = 0; x < nWidth; ++x)

{

var xOffset = pt[x, y].X;

var yOffset = pt[x, y].Y;

if (yOffset >= 0 && yOffset < nHeight && xOffset >= 0 && xOffset < nWidth)

{

if (pSrc != null)

{

p[0] = pSrc[yOffset * scanline + xOffset * 3];

p[1] = pSrc[yOffset * scanline + xOffset * 3 + 1];

p[2] = pSrc[yOffset * scanline + xOffset * 3 + 2];

}

}

p += 3;

}

p += nOffset;

}

}

baseMap.UnlockBits(bitmapData);

bSrc.UnlockBits(bmSrc);

bSrc.Dispose();

}

}

}

}

这么长的代码你竟然看完了!

有一些要指出的地方:

1

字符集并不包含全部的字母和数字,这是因为有些数字和英文字母难以区分,比如:

数字0和字母O

数字5和字母S

数字1和字母I

2

我注释掉了DrawDisorderLine()方法中的贝塞尔曲线,这是因为当验证码图片非常小的时候,贝塞尔曲线会干扰字符显示,看不清验证码。

现在,在你的MVC控制器中,创建一个Action用于返回验证码图片:

[Route("get-captcha-image")]

public IActionResult GetCaptchaImage()

{

int width = 100;

int height = 36;

var captchaCode = Captcha.GenerateCaptchaCode();

var result = Captcha.GenerateCaptchaImage(width, height, captchaCode);

HttpContext.Session.SetString("CaptchaCode", result.CaptchaCode);

Stream s = new MemoryStream(result.CaptchaByteData);

return new FileStreamResult(s, "image/png");

}

现在,尝试访问这个Action,你应该能看到像这样的验证码图片:

3

使用验证码

在你需要提交内容到服务器端的model里加入一个新的属性,叫做CaptchaCode

[Required]

[StringLength(4)]

public string CaptchaCode { get; set; }

在View中加入一个对应CaptchaCode的输入框,以及一个调用GetCaptchaImage的图片

<div class="input-group">

<div class="input-group-prepend">

<img id="img-captcha" data-src="~/get-captcha-image" />

</div>

<input type="text" class="form-control" placeholder="Captcha Code" asp-for="CaptchaCode" maxlength="4" />

<span asp-validation-for="CaptchaCode" class="text-danger"></span>

</div>

在处理用户提交数据的Action中加入检查验证码的逻辑

if (ModelState.IsValid)

{

// Validate Captcha Code

if (!Captcha.ValidateCaptchaCode(model.CaptchaCode, HttpContext))

{

// return error

}

// continue business logic

}

4

再完善一点

你可以用jQuery实现用户点击图片刷新验证码

$("#img-captcha").click(function () {

resetCaptchaImage();

});

function resetCaptchaImage() {

d = new Date();

$("#img-captcha").attr("src", "/get-captcha-image?" + d.getTime());

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。