Reputation: 1292
I am developing asp.net MVC 6 application in asp.net core , and i want to create a captcha for my login page. in previous .net frameworks i used the system.drawing to create a captcha but since in .net framework core we dont have system.drawing , how can i achieve this ?
one solution is to reference full .net framework but this is not what i want. i want to use the core framework.
another one is to create a project with .net framework 6 and Mvc 5 and use a web api to get the captcha image , but also this is not what i want.
is there another solution ?
Upvotes: 11
Views: 24305
Reputation: 215
I've done this:
Add a reference to System.Drawing.Common
Install linux libs if on linux:
sudo apt install libc6-dev
sudo apt install libgdiplus
[AllowAnonymous]
public async Task<IActionResult> GetCaptchaImage()
{
var validate = "";
var bmp = ImageCaptcha.Generate(200, 150, out validate);
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(ms.ToArray());
result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
SetCookie("CV", validate.Hash()+"");
return File(ms.ToArray(),"image/png");
}
}
// move to base controller
public void SetCookie(string key, string value, int? expireTime = null)
{
CookieOptions option = new CookieOptions();
if (expireTime.HasValue)
option.Expires = DateTime.Now.AddMinutes(expireTime.Value);
else
option.Expires = DateTime.Now.AddMilliseconds(5 * 60 * 1000);
Response.Cookies.Append(key, value, option);
}
Add this class to project - generates random rotated letters bmp in mem:
public static class ImageCaptcha
{
public static Bitmap Generate(int w, int h, out string validate)
{
Bitmap bmp = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bmp);
var brush = new[] { Brushes.Brown, Brushes.LawnGreen, Brushes.Blue, Brushes.Coral};
var rand = new Random((int)DateTime.Now.Ticks);
validate = null;
using (Graphics g = Graphics.FromImage(bmp))
{
for (int i = 0; i < 4; i++)
{
var text = Convert.ToChar( rand.Next(97, 122)).ToString();
validate += text;
if(i==0) g.TranslateTransform(bmp.Width / 2, bmp.Height / 2);
g.RotateTransform(40 * 5 * rand.Next(1, 6));
var font = new Font("Arial", h * 0.25f + 8 * rand.Next(1, 6), FontStyle.Bold);
SizeF textSize = g.MeasureString(text, font);
g.DrawString(text, font, brush[i],15*(i+1) -(textSize.Width / 2), -(textSize.Height / 2));
}
}
return bmp;
}
}
Add this utility class to get md5 hash easy:
public static partial class H
{
public static int Hash(this string value)
{
MD5 md5Hasher = MD5.Create();
var hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(value));
var ivalue = BitConverter.ToInt32(hashed, 0);
return ivalue;
}
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
var validCaptchaText = Request.Cookies["cv"]; //added
if (ModelState.IsValid && model.UserCaptchaText.Hash()+""==validCaptchaText) //changed
{
var user = new AppUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
<div class="form-group">
<label>Captcha</label><br />
<img style="width:100%; border:solid 1px #ced4da" height="150" src="@Url.Action("GetCaptchaImage")" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
<ul style="list-style-type:none">
<li style="width:24px;height:12px; display:inline-block; background-color:#a52a2a "></li>
<li style="width:24px;height:12px; display:inline-block; background-color:#7cfc00 "></li>
<li style="width:24px;height:12px; display:inline-block; background-color:#0000ff "></li>
<li style="width:24px;height:12px; display:inline-block; background-color:#ff7f50 "></li>
</ul>
</div>
<div class="form-group">
<label asp-for="UserCaptchaText"></label>
<input asp-for="UserCaptchaText" class="form-control" />
</div>
<button style="color:white; background-color:#4a7dbc" type="submit" class="btn btn-default">Register</button>
And done.
Upvotes: 4
Reputation: 11
To easy, you can user that component, You can see how use in that link https://www.nuget.org/packages/IgniteCaptcha/1.0.0
Upvotes: 1
Reputation: 1163
You can do it without any special packaging. According to the recaptcha site -
Client Side Integration
Paste this snippet before the closing </head>
tag on your HTML template:
<script src='https://www.google.com/recaptcha/api.js'></script>
Paste this snippet at the end of the where you want the reCAPTCHA widget to appear:
<div class="g-recaptcha" data-sitekey="YOURSITEKEY"></div>
Server Side Integration
When your users submit the form where you integrated reCAPTCHA, you'll get as part of the payload a string with the name "g-recaptcha-response". In order to check whether Google has verified that user, send a POST request with these parameters:
URL: https://www.google.com/recaptcha/api/siteverify
secret (required): YOURSECRETKEY
response (required): The value of 'g-recaptcha-response'.
remoteip: The end user's ip address.
Upvotes: 3
Reputation: 705
I made a wrapper for recaptcha that works with aspnetcore, check it out https://www.nuget.org/packages/PaulMiami.AspNetCore.Mvc.Recaptcha and there is some documentation at https://github.com/PaulMiami/reCAPTCHA/wiki/Getting-started
In short you need to do the following to use it:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddRecaptcha(new RecaptchaOptions
{
SiteKey = Configuration["Recaptcha:SiteKey"],
SecretKey = Configuration["Recaptcha:SecretKey"]
});
}
2. In your "_ViewImports.cshtml" file add.
@addTagHelper *, PaulMiami.AspNetCore.Mvc.Recaptcha
3. In your view.
<form asp-controller="Home" asp-action="Index" method="post">
<recaptcha />
<input type="submit" value="submit" />
</form>
@section Scripts {
<recaptcha-script />
}
4. In your controller.
[ValidateRecaptcha]
[HttpPost]
public IActionResult Index(YourViewModel viewModel)
{
if (ModelState.IsValid)
{
return new OkResult();
}
return View();
}
Upvotes: 9
Reputation: 36696
I implemented Recaptcha in my ASP.NET Core apps. In my login view:
@if (Model.RecaptchaSiteKey.Length > 0)
{
<script src='https://www.google.com/recaptcha/api.js'></script>
}
@if (Model.RecaptchaSiteKey.Length > 0)
{
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="g-recaptcha" data-sitekey="@Model.RecaptchaSiteKey"></div>
@Html.ValidationMessage("recaptchaerror", new { @class = "text-danger" })
</div>
</div>
}
I implemented an extension method on controller so I can validate the captcha server side easily from any controller where I'm using it.
public static async Task<RecaptchaResponse> ValidateRecaptcha(
this Controller controller,
HttpRequest request,
string secretKey)
{
var response = request.Form["g-recaptcha-response"];
var client = new HttpClient();
string result = await client.GetStringAsync(
string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}",
secretKey,
response)
);
var captchaResponse = JsonConvert.DeserializeObject<RecaptchaResponse>(result);
return captchaResponse;
}
Then this snippet from the login post method in my AccountController checks the captcha server side using that extension method:
if ((Site.CaptchaOnLogin) && (Site.RecaptchaPublicKey.Length > 0))
{
var recpatchaSecretKey = Site.RecaptchaPrivateKey;
var captchaResponse = await this.ValidateRecaptcha(Request, recpatchaSecretKey);
if (!captchaResponse.Success)
{
ModelState.AddModelError("recaptchaerror", "reCAPTCHA Error occured. Please try again");
return View(model);
}
}
Note that to call extension methods on a controller you have to use the this keyword.
I'm using this in multiple projects currently so if you need to see more code, the most simple is in my SimpleAuth project, but I'm also using it cloudscribe
Upvotes: 11
Reputation: 506
Please follow the below steps to create Captcha in Mvc.
Add the CaptchaMvc library to the Reference Layer in your application like this.
Now create the Index View like this.
View
@using CaptchaMvc.HtmlHelpers
@using MathCaptcha;
@using CaptchaMvc;
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Captcha Example</legend>
@Html.MathCaptcha()
@*@Html.Captcha(3)*@
<input type="submit" value="Send" />
</fieldset>
}
@Html.MathCaptcha() helper class then it will generate a mathmatical CAPTCHA. If you using the @Html. Captcha(3) helper class then it will generate a Char CAPTCHA. 3 is the length of the CAPTCHA.
In the post action method I wrote code for CAPTCHA validation.
[HttpPost]
public ActionResult Index(string empty)
{
// Code to validate the CAPTCHA image
if (this.IsCaptchaValid("Captcha not valid"))
{
// do your stuff
}
return View();
}
Upvotes: 0