C# 版 ReCaptcha 解决方案,无外部 JavaScript 插件
易于使用的纯 C# 代码 ReCaptcha 工具,适用于 Web 应用程序,无需外部 JavaScript 插件
引言
CAPTCHA 实际上是“Completely Automated Public Turing test to tell bots and Humans Apart”(区分机器人和人类的完全自动化的公共图灵测试)的缩写。 人类很容易解决,但“机器人”和其他恶意软件很难破解。
我们通常使用 ReCaptcha 来保护我们的 Web 应用程序免受机器人和垃圾邮件的侵害。 通常,大多数开放网站都使用 Google ReCaptcha 服务来保护其网站免受机器人交互。
然而,问题在于安全性,因为 Google recaptcha 是基于纯 JavaScript API 工作的,它会在运行时将 JavaScript 代码注入到您网站的相应页面中以生成 recaptcha 的图像。 允许外部 JavaScript 在运行时注入您的 Web 应用程序可能存在风险。
此外,要使用 Google 的免费 rechaptcha API,我们需要将外部 JavaScript 整合到我们的环境和 Web 应用程序中,这对于不允许使用外部 JavaScript 的安全企业应用程序来说存在风险。
因此,这里是无需任何外部第三方插件(如 Google reCAPTCHA 等)即可构建自己的 ReCaptcha 的完整解决方案。
这是一个用 GenricHandler、MVC 4.0、Bootstrap、Jquery AJAX 和 CSS 开发的简单代码。
在示例中,我有两种解决方案 - 一种用于基本使用,另一种用于复杂使用。
- 简单 ReCaptcha
- 复杂 ReCaptcha
注意: 它需要 Bootstrap 包来设计。 从此处获取 Bootstrap。
输出演示如下所示。
演示
背景
在当今的互联网世界中,ReCaptcha 等工具对于每个 Web 应用程序都至关重要。 它们保护 Web 应用程序免受恶意攻击或机器人与 Web 应用程序的 Web 表单的交互。 因此,大多数 Web 应用程序都使用 Google 等提供的免费 recapcha 服务,这些服务会处理运行时 JavaScript。
然而,一些应用程序会创建自己的 recapcha 工具来支持和保护其 Web 应用程序免受机器人不必要的活动。
此解决方案还通过使用通用处理程序和 C# 代码解决了这个问题,并能够创建功能齐全的 recapcha 代码供使用。
Using the Code
代码从基本的 Web 页面开始,包括四个主要页面,以及一个用于复杂 recapcha 代码的额外部分。
- _Layout.cshtml
- Index.cshtml
- HomeContoller.cs
- GenerateRecaptcha.ashx(简单 Recaptcha 代码)
- GenerateRecaptcha2.ashx(复杂 Recaptcha 代码)
步骤 1
要创建这个小型 recapcha 工具,首先文件夹结构非常重要,因为我在这里使用的是 MVC。
为您的 Web 应用程序创建如下所示的文件夹结构。
文件夹结构
第二步
创建 _Layout.cshtml 文件(MVC 模板默认的主文件 - 只需根据您的需要进行编辑)。
_Layout.cshtml 文件提供主页面布局,并包含所有必需的 jQuery 或 CSS,如 bootstrap.css。
这是一个供您参考的主页选项,它将帮助您将 recapcha 工具集成到您的 Web 应用程序中。
_Layout.cshtml 的代码如下所示。
_Layout.cshtml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
<link href="~/Content/css/bootstrap.css" rel="stylesheet" />
<script src="~/Content/js/jquery-1.11.1.js"></script>
<script src="~/Content/js/Common.js"></script>
</head>
<body class="container">
<div class="">
<h1>Recaptcha Demo <small>.Net Recaptcha with MVC and Responsive
Design with bootstrap</small></h1>
</div>
<div>
<div id="body">
@RenderSection("featured", required: false)
<section class="container-fluid">
@RenderBody()
</section>
</div>
</div>
<footer>
<div class="container-fluid">
<div class="left">
<p>MVC </p>
</div>
</div>
</footer>
@RenderSection("scripts", required: false)
</body>
</html>
步骤 3
我们需要另一个页面来使用 recapcha 工具,所以我使用默认的 MVC 索引页面来处理我的 recapcha 请求并提供一个容器来容纳它。
Index.cshtml 的代码如下所示。
Index.cshtml
@model ReCaptchaDemo.Models.Employee
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<style type="text/css">
.Recaptcha
{
background-color: #428bca;
border: 5px solid #428bca;
border-radius: 5px;
width: 295px;
}
.RecaptchaEnterCode
{
margin-top: 15px;
background-color: #f5f5f5;
border: 1px solid #e3e3e3;
border-radius: 8px;
padding: 8px;
}
.Recaptcha2
{
background-color: darkred;
border: 5px solid darkred;
border-radius: 5px;
width: 245px;
}
.RecaptchaEnterCode2
{
margin-top: 15px;
background-color: cornsilk;
border: 1px solid cornsilk;
border-radius: 8px;
padding: 8px;
}
</style>`
'<div class="panel panel-primary">`
<div class="panel-heading">
<h3 class="panel-title">Form With Recaptcha</h3>
</div>
<div class="panel-body row">
@using (Html.BeginForm("ValidateRecaptcha2", "Home",
FormMethod.Post, new { id = "mvcForm2" }))
{
<div class="col-xs-6">
<h3>Simple reCAPTCHA</h3>
<div class="form-horizontal well" role="form">
<div class="form-group">
<label class="col-xs-4 control-label">Fisrt Name</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.FirstName,
new { @class = "form-control",
id = "fName", placeholder = "First Name",
type = "text" })
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">Last Name</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.LastName,
new { @class = "form- control", id = "lName",
placeholder = "Last Name", type = "text" })
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">Email</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.Email,
new { @class = "form-control", id = "email",
placeholder = "Email", type = "email" })
</div>
</div>
<div class="form-group">
<div class="col-xs-12 col-xs-offset-2">
<div class="Recaptcha2">
<table class="">
<tr style="background-color: white;
width: 235px !important">
<td>
<div>
<img src=""
alt="ReCaptchaImage"
id="reCaptchaImage2"
class="img-responsive" />
</div>
</td>
<td>
<div>
<div class="btn-group-vertical">
<span class="btn btn-default btn-xs"
title="Help"><i
class="glyphicon glyphicon-info-sign"></i></span>
<span id="reloadCaptcha2"
class="btn btn-warning btn-sm"
title="Reload captcha image"><i
cl<span class="btn btn-default
btn-xs"><i
class="glyphicon glyphicon-headphones"
title="Speak - development in progress"></i>
</span>
</div>
</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="RecaptchaEnterCode2">
ReCaptcha text enter below
@Html.TextBoxFor(m => m.ReCaptchaCode,
new { @class = "form-control input-sm",
id = "reCaptcha",
placeholder = "Type text ",
type = "text" })
</div>
</td>
<td></td>
</tr>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<button type="submit"
class="btn btn-warning" name="Action"
value="ValidateCaptcha2">Validate ReCaptcha</button>
</div>
<div class="col-xs-8">
@if (!string.IsNullOrEmpty(@ViewBag.RechaptchaMessage2))
{
<div class="alert alert- warning">@ViewBag.RechaptchaMessage2</div>
}
</div>
</div>
</div>
</div>
}
@using (Html.BeginForm("ValidateRecaptcha", "Home",
FormMethod.Post, new { id = "mvcForm" }))
{
<div class="col-xs-6">
<h3>Extreme reCAPTCHA</h3>
<div class="form-horizontal well" role="form">
<div class="form-group">
<label class="col-xs-4 control-label">Fisrt Name</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.FirstName,
new { @class = "form- control", id = "fName",
placeholder = "First Name", type = "text" })
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">Last Name</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.LastName, new {
@class = "form-control", id = "lName",
placeholder = "Last Name", type = "text" })
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">Email</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.Email, new {
@class = "form-control", id = "email",
placeholder = "Email", type = "email" })
</div>
</div>
<div class="form-group">
<div class="col-xs-12 col-xs-offset-2">
<div class="Recaptcha">
<table class="">
<tr style="background-color: white;
min-width: 285px !important">
<td>
<div>
<img src="" alt="ReCaptchaImage"
id="reCaptchaImage" class="img-responsive" />
</div>
</td>
<td>
<div>
<div class="btn-group-vertical">
<span class="btn btn-default btn-xs"
title="Help">
<i class="glyphicon glyphicon-info-sign"></i>
</span>
<span id="reloadCaptcha"
class="btn btn-warning btn-sm"
title="Reload captcha image">
<i class="glyphicon glyphicon-refresh"></i>
</span>
<span class="btn btn-default btn-xs"
title="Speak - development in progress">
<i class="glyphicon glyphicon-headphones"></i>
</span>
</div>
</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="RecaptchaEnterCode">
ReCaptcha text enter below
@Html.TextBoxFor(m => m.ReCaptchaCode,
new { @class = "form-control input-sm",
id = "reCaptcha",
placeholder = "Type text ", type = "text" })
</div>
</td>
<td></td>
</tr>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<button type="submit" class="btn btn-primary"
name="Action"
value="ValidateCaptcha1">Validate ReCaptcha</button>
</div>
<div class="col-xs-8">
@if (!string.IsNullOrEmpty(@ViewBag.RechaptchaMessage1))
{
<div class="alert alert-info">@ViewBag.RechaptchaMessage1
</div>
}
</div>
</div>
</div>
</div>
}
</div>
<div class="panel-footer">©
@DateTime.Now.Year - Recaptcha Demo By Shiv</div>
</div>
步骤 4
任何 MVC 项目的核心部分是控制器。它负责视图和处理程序之间的通信。由于我使用处理程序来创建 recapcha 图像并进行处理以便在视图中可用,因此控制器可以为我们完成工作。
正如您所见,控制器代码直接调用 httphandler
来获取图像信息,并将其作为 json 结果传递给 Index.cshtml 视图,如前所述。由于我使用的是 MVC 的默认控制器,其名称为 HomeController.cs,我们可以根据需要进行修改。
HomeController.cs 的代码如下所示。
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ReCaptchaDemo.Models;
namespace ReCaptchaDemo.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public JsonResult GetRecaptchaImage()
{
string re = "GenerateRecaptcha.ashx?RcId=" + Guid.NewGuid();
return Json(re);
}
public JsonResult GetRecaptchaImage2()
{
string re = "GenerateRecaptcha2.ashx?RcId=" + Guid.NewGuid();
return Json(re);
}
[HttpPost]
public ActionResult ValidateRecaptcha(Employee employee)
{
string msg;
if (HttpContext.Session != null &&
Convert.ToString(HttpContext.Session["captcha"]) == employee.ReCaptchaCode)
{
msg = "Recaptcha Validation Success";
}
else
{
msg = "Recaptcha Validation Fail";
}
ViewBag.RechaptchaMessage1 = msg;
return View("Index");
}
[HttpPost]
public ActionResult ValidateRecaptcha2(Employee employee)
{
string msg;
if (HttpContext.Session != null &&
Convert.ToString(HttpContext.Session["captcha2"]) ==
employee.ReCaptchaCode)
{
msg = "Recaptcha Validation Success";
}
else
{
msg = "Recaptcha Validation Fail";
}
ViewBag.RechaptchaMessage2 = msg;
return View("Index");
}
}
}
步骤 5
创建简单的 Recaptcha
为了创建 recapcha 的图像,我使用了一个通用处理程序来提供所需的输出,在我的例子中是图像。
通用处理程序基本上用于当对 ASP.NET Web 应用程序的请求响应时运行的过程。
通用处理程序的扩展名为 ASHX。它们相当于用 C# 或 Visual Basic.NET 编写的自定义处理程序,因为它们包含完全实现 IHttpHandler
的类。它们像 ASPX 文件一样方便。您只需访问它们,它们就会自动编译。
就像 ASPX 文件可以即时(即时)编译一样,处理程序也可以。
有关 httphandler
的更多信息,请访问此链接
- http://www.brainbell.com/tutorials/ASP/Generic_Handlers_%28ASHX_Files%29.html
- https://msdn.microsoft.com/en-us/library/bb398986.aspx
ashx 处理程序的一些功能
动态图像生成:您可以编写处理程序来返回数据驱动的图像,方法是创建一个返回图像数据的 ASHX 处理程序,然后在您的标签中使用该 URL。
e.g.<img alt="user's custom icon" src="Icon.ashx?username=bob"></img>
向客户端的 AJAX 代码返回基于 REST 的 XML 或 JSON 数据。
自定义 HTML:当 ASP.NET Web Forms 或 MVC 框架过于严格时,为页面返回完全自定义的 HTML。
GenerateRecaptcha.ashx 的代码如下所示 。
GenerateRecaptcha.ashx - 简单的 ReCaptcha
using System;
using System.Web;
using System.Drawing;
using System.Drawing.Text;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Web.SessionState;
namespace ReCaptchaDemo
{
/// <summary>
/// Summary description for GenerateRecaptcha
/// </summary>
public class GenerateRecaptcha : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
int captchaStr = new Random().Next(6, 12);
context.Session["captcha"] = GenerateRandomString(captchaStr);
const int iHeight = 70;
const int iWidth = 250;
var oRandom = new Random();
int[] aFontEmSizes = { 15, 20, 25, 30 };
string[] aFontNames = { "Comic Sans MS", "Arial",
"Times New Roman", "Georgia", "Verdana", "Geneva" };
FontStyle[] aFontStyles =
{
FontStyle.Bold,
FontStyle.Italic,
FontStyle.Regular,
FontStyle.Strikeout,
FontStyle.Underline
};
HatchStyle[] aHatchStyles =
{
HatchStyle.BackwardDiagonal, HatchStyle.Cross,
HatchStyle.DashedDownwardDiagonal, HatchStyle.DashedHorizontal,
HatchStyle.DashedUpwardDiagonal, HatchStyle.DashedVertical,
HatchStyle.DiagonalBrick, HatchStyle.DiagonalCross,
HatchStyle.Divot, HatchStyle.DottedDiamond, HatchStyle.DottedGrid,
HatchStyle.ForwardDiagonal, HatchStyle.Horizontal,
HatchStyle.HorizontalBrick, HatchStyle.LargeCheckerBoard,
HatchStyle.LargeConfetti, HatchStyle.LargeGrid,
HatchStyle.LightDownwardDiagonal, HatchStyle.LightHorizontal,
HatchStyle.LightUpwardDiagonal, HatchStyle.LightVertical,
HatchStyle.Max, HatchStyle.Min, HatchStyle.NarrowHorizontal,
HatchStyle.NarrowVertical, HatchStyle.OutlinedDiamond,
HatchStyle.Plaid, HatchStyle.Shingle, HatchStyle.SmallCheckerBoard,
HatchStyle.SmallConfetti, HatchStyle.SmallGrid,
HatchStyle.SolidDiamond, HatchStyle.Sphere, HatchStyle.Trellis,
HatchStyle.Vertical, HatchStyle.Wave, HatchStyle.Weave,
HatchStyle.WideDownwardDiagonal, HatchStyle.WideUpwardDiagonal,
HatchStyle.ZigZag
};
//Get Captcha in Session
string sCaptchaText = context.Session["captcha"].ToString();
//Creates an output Bitmap
var oOutputBitmap = new Bitmap(iWidth, iHeight, PixelFormat.Format24bppRgb);
var oGraphics = Graphics.FromImage(oOutputBitmap);
oGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
//Create a Drawing area
var oRectangleF = new RectangleF(0, 0, iWidth, iHeight);
//Draw background (Lighter colors RGB 100 to 255)
Brush oBrush = new HatchBrush
(aHatchStyles[oRandom.Next(aHatchStyles.Length - 1)], Color.Gainsboro,
Color.White);//HatchBrush(aHatchStyles[oRandom.Next(aHatchStyles.Length - 1)],
Color.FromArgb((oRandom.Next(100, 255)), (oRandom.Next(100, 255)),
(oRandom.Next(100, 255))), Color.White);
oGraphics.FillRectangle(oBrush, oRectangleF);
var oMatrix = new Matrix();
int i;
for (i = 0; i <= sCaptchaText.Length - 1; i++)
{
oMatrix.Reset();
int iChars = sCaptchaText.Length;
int x = iWidth / (iChars + 1) * i;
const int y = iHeight / 2;
//Rotate text Random
oMatrix.RotateAt(oRandom.Next(-40, 40), new PointF(x, y));
oGraphics.Transform = oMatrix;
//Draw the letters with Randon Font Type, Size and Color
oGraphics.DrawString
(
//Text
sCaptchaText.Substring(i, 1),
//Random Font Name and Style
new Font(aFontNames[oRandom.Next(aFontNames.Length - 1)],
aFontEmSizes[oRandom.Next(aFontEmSizes.Length - 1)],
aFontStyles[oRandom.Next(aFontStyles.Length - 1)]),
//Random Color (Darker colors RGB 0 to 100)
new SolidBrush(Color.FromArgb
(oRandom.Next(0, 100), oRandom.Next(0, 100), oRandom.Next(0, 100))),
x,
oRandom.Next(10, 40)
);
oGraphics.ResetTransform();
}
context.Response.ContentType = "image/JPEG";
//render image
oOutputBitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
//dispose everything, we do not need them any more.
oOutputBitmap.Dispose();
oGraphics.Dispose();
Console.WriteLine();
context.Response.End();
}
public bool IsReusable
{
get
{
return false;
}
}
public static string GenerateRandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz";
string result = "";
var rand = new Random();
for (int i = 0; i < length; i++)
{
result += chars[rand.Next(chars.Length)];
}
return result;
}
}
}
GenerateRecaptcha2.ashx 的代码如下所示。
GenerateRecaptcha2.ashx - 复杂的 ReCaptcha
using System;
using System.Web;
using System.Drawing;
using System.Drawing.Text;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Web.SessionState;
namespace ReCaptchaDemo
{
/// <summary>
/// Summary description for GenerateRecaptcha
/// </summary>
public class GenerateRecaptcha2 : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
Bitmap objBMP = new Bitmap(200, 60);
Graphics objGraphics = Graphics.FromImage(objBMP);
objGraphics.Clear(Color.White);
objGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
objGraphics.CompositingQuality = CompositingQuality.HighQuality;
objGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
//' Configure font to use for text
Font objFont = new Font("Georgia", 26, FontStyle.Italic);
//generate a random string for captcha
string randomStr = GenerateRandomString(8);
//This is to add the string to session cookie, to be compared later
HttpContext.Current.Session.Add("captcha2", randomStr);
//' Write out the text
objGraphics.DrawString(randomStr, objFont, Brushes.Black, 3, 3);
//' Set the content type and return the image
context.Response.ContentType = "image/JPEG";
//render image
objBMP.Save(context.Response.OutputStream, ImageFormat.Jpeg);
//dispose everything, we do not need them any more.
objFont.Dispose();
objGraphics.Dispose();
objBMP.Dispose();
}
public bool IsReusable
{
get
{
return false;
}
}
public static string GenerateRandomString(int length)
{
const string chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz";
string result = "";
var rand = new Random();
for (int i = 0; i < length; i++)
{
result += chars[rand.Next(chars.Length)];
}
return result;
}
}
}
关注点
在实现此解决方案时,我喜欢看到 System.Drawing;
和 .NET 程序集的图形功能。这很有趣,有时我喜欢用这些程序集支持的代码来创建不同的图像和输出。