Septiembre 21

2017

En esta ocasión os traigo una solución simple al tema de los captchas (preguntistas para demostrar que eres humano) para c# en ASP.

Normalmente para estas cosas, se suele utilizar a Google y su API reCaptcha, que ahora va por la versión 2. Pero desde mi punto de vista esta API muchas veces es difícil de implementar, y sobre todo, difícil de gestionar desde aplicaciones WebForms.

Con este control de usuario puedes implementar un captcha en el formato de pregunta respuesta o calculo de una simple ecuación.

Como Usarlo

Para poder usar el control desde cualquier lugar de nuestra pagina web, lo primero que debemos de hacer el modificar el web.config para referenciar el control. Aquí os dejo el código:
  <system.web>
<pages>
<controls>
<add tagPrefix="uc" src="~/Utils/Captcha.ascx" tagName="Captcha"/>
</controls>
</pages>
</system.web>

Evidentemente la carpeta utils puedes cambiarla por la carpeta donde instales el control de usuario. Luego lo insertamos en nuestra pagina web con esta instruccion, la cual si que no cambiara aunque lo referencies en otra carpeta.

<uc:Captcha ID="Captcha0" runat="server" />

Solamente con esto ya el control esta listo para su uso, pero tenemos unas cuantas propiedades para personalizar el control.

Propiedades

  • Estilo: Numérico, Capitales
  • ClaseContenedor: Cambiar la clase para contener el control
  • ClasePregunta: Asignar la clase para el texto de la pregunta
  • ClaseRespuesta: Asignar una clase para la respuesta
  • WidthRespuesta: indicar el ancho del cuadro de texto
  • ColorFondoRespuesta: indicar el color a utilizar para el cuadro de texto de la respuesta
  • Validar: comprueba que es valido el captcha

Metodos

  • SetCapitales: asignar las los textos País/Capital definidos por el usuario, por defecto ya se utilizan 22 capitales de varios continentes, las cuales no tienen espacios ni caracteres especiales como acentos u otros. Lógicamente si no quieres utilizar países y capitales puedes asignar un texto como pregunta y otro como respuesta, pero ten en cuenta que el usuario debe de poder saber la respuesta fácilmente.

Ejemplo de uso

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="Pub_Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<!-- Control de usuario Captcha -->
<uc:Captcha ID="captcha" runat="server" Estilo="Numerico" />
<!-- Mensaje si es correcto o no -->
<asp:Label ID="lblError" Text="" runat="server" />
<!-- Boton para invocar la comprobacion -->
<asp:Button ID="valida" Text="Click" runat="server" OnClick="valida_Click"/>
</form>
</body>
</html>

Como puedes comprobar cuando se declara el control de usuario ya le asigno el Estilo a utilizar es la mejor forma de inicializar el captcha.

Default.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class Pub_Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//Response.Redirect("~/");
if (!Page.IsPostBack)
{
List<Captcha.Mundo> capitales = new List<Captcha.Mundo>();
capitales.Add(new Captcha.Mundo("francia", "paris"));
capitales.Add(new Captcha.Mundo("japon", "tokio"));
capitales.Add(new Captcha.Mundo("españa", "madrid"));
capitales.Add(new Captcha.Mundo("italia", "roma"));
captcha.SetCapitales(capitales);
}
}

protected void valida_Click(object sender, EventArgs e)
{
if (captcha.Validar) lblError.Text = "ok";
else lblError.Text = "error";
}
}

No hay mucho que comentar en el código.  En esta ocasión he inicializado unas cuantos países y sus capitales.


El código al completo

Captcha.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="captcha.ascx.cs" Inherits="Captcha" %>

<style type="text/css">
.DrCaptcha {
background-color: transparent;
display:inline-block;
line-height: 20px;
font-size: 14px;
font-family: Courier New, Courier, monospace, sans-serif;
}

.DrCaptcha input[type='text'] {
border-radius: 4px;
padding: 10px;
outline: none;
border: none;
box-sizing: border-box;
margin: 0;
}
.DrCaptcha input[type='text']:hover, .DrCaptcha input[type='text']:active, .DrCaptcha input[type='text']:focus {
border: 1px solid #f0efef;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
}
</style>

<asp:Panel ID="Ventana" CssClass="DrCaptcha" runat="server">

<asp:Label ID="lblSMS" Text="" runat="server" />
<asp:TextBox ID="txtValidacion" runat="server"></asp:TextBox>
</asp:Panel>

Captcha.ascx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class Captcha : UserControl
{
#region Propiedades
public enum Estilos
{
Numerico = 1,
Capitales = 2
}

private Estilos estilo;
public Estilos Estilo
{
get { return estilo; }
set { estilo = value; }
}

private bool Valido;
public bool Validar
{
get
{
Valido = validar();
return Valido;
}
set { Valido = value; }
}

private string cssContenedor;
public string ClaseContenedor
{
get { return cssContenedor; }
set { cssContenedor = value; }
}

private string cssSMS;
public string ClasePregunta
{
get { return cssSMS; }
set { cssSMS = value; }
}

private string cssTexto;
public string ClaseRespuesta
{
get { return cssTexto; }
set { cssTexto = value; }
}
private Unit anchoTexto;

public Unit WidthRespuesta
{
get { return anchoTexto; }
set { anchoTexto = value; }
}

private System.Drawing.Color backColor;
public System.Drawing.Color ColorFondoRespuesta
{
get { return backColor; }
set { backColor = value; }
}

#endregion

#region Metodos
public void SetCapitales(List<Mundo> capitales)
{
this.Capitales = capitales;
}
#endregion

#region Variables
private List<Mundo> Capitales;
private int pos;
#endregion

#region Funciones
private bool validar()
{
bool retorno;
txtValidacion.BackColor = this.ColorFondoRespuesta;
if (string.IsNullOrEmpty(txtValidacion.Text)) retorno = false;
else
{
switch (estilo)
{
case Estilos.Numerico:
retorno = ValidaNumerico();
break;
case Estilos.Capitales:
retorno = ValidaCapitales();
break;
default:
retorno = false;
break;
}
}
if (retorno == false) txtValidacion.BackColor = System.Drawing.Color.Orange;

return retorno;
}

private bool ValidaNumerico()
{
bool resultado;
try
{
resultado = (int)Session["CaptchaResultado"] == Convert.ToInt32(txtValidacion.Text);
}
catch
{
resultado = false;
}

if (resultado == false) CrearEcuacion();

return resultado;
}

/// <summary>
/// Comprobar si el texto introducido coincide con la capital seleccionada
/// </summary>
/// <returns></returns>
private bool ValidaCapitales()
{
bool resultado;
try
{
int pos = (int)Session["CaptchaPos"];
if (((Mundo)Capitales[pos]).Capital == txtValidacion.Text) resultado = true;
else
{
resultado = false;
SelCapital();
}
}
catch
{
resultado = false;
}
return resultado;
}

/// <summary>
/// Crear una ecuacion sencilla aleatoria
/// </summary>
private void CrearEcuacion()
{
int num1 = NumeroAleatorio(9);
int num2 = NumeroAleatorio(9);
int operador = NumeroAleatorio(3);
int resultado;

//impedir que los numeros sean iguales
do
{
num2 = NumeroAleatorio(9);
} while (num1 == num2);

switch (operador)
{
case 1: //restar
if (num1 > num2)
{
resultado = num1 - num2;
lblSMS.Text = num1.ToString("N0") + " - " + num2.ToString("N0") + " = ";
}
else
{
resultado = num2 - num1;
lblSMS.Text = num2.ToString("N0") + " - " + num1.ToString("N0") + " = ";
}
break;
case 2: //multiplicar
resultado = num1 * num2;
lblSMS.Text = num1.ToString("N0") + " x " + num2.ToString("N0") + " = ";
break;
case 3: //dividir
if (num2 == 0)
{
//impedir division por 0
do
{
num2 = NumeroAleatorio(9);
} while (num2 == 0);
}
num1 = num1 * num2; //hacer que la division sea siempre exacta
resultado = num1 / num2;
lblSMS.Text = num1.ToString("N0") + " / " + num2.ToString("N0") + " = ";
break;
default: //sumar
resultado = num1 + num2;
lblSMS.Text = num1.ToString("N0") + " + " + num2.ToString("N0") + " = ";
break;
}
//almacenar los resultados
Session["CaptchaResultado"] = resultado;
Session["CaptchaEcuacion"] = lblSMS.Text;
}

/// <summary>
/// Define las capitales por defecto
/// </summary>
private void CrearCapitales()
{
List<Mundo> capitales = new List<Mundo>();
capitales.Add(new Captcha.Mundo("España", "Madrid"));
capitales.Add(new Captcha.Mundo("Italia", "Roma"));
capitales.Add(new Captcha.Mundo("Brasil", "Brasilia"));
capitales.Add(new Captcha.Mundo("Venezuela", "Caracas"));
capitales.Add(new Captcha.Mundo("Ecuador", "Quito"));
capitales.Add(new Captcha.Mundo("Bolivia", "Chile"));
capitales.Add(new Captcha.Mundo("Suriman", "Paramaribo"));
capitales.Add(new Captcha.Mundo("Uruguay", "Montevideo"));
capitales.Add(new Captcha.Mundo("Guyana", "Georgetown"));
capitales.Add(new Captcha.Mundo("Portugal", "Lisboa"));
capitales.Add(new Captcha.Mundo("Jamaica", "Kingston"));
capitales.Add(new Captcha.Mundo("Nicaragua", "Managua"));
capitales.Add(new Captcha.Mundo("Kuwait", "Kuwait"));
capitales.Add(new Captcha.Mundo("Chad", "Yamena"));
capitales.Add(new Captcha.Mundo("Ghana", "Acra"));
capitales.Add(new Captcha.Mundo("Kenia", "Nairobi"));
capitales.Add(new Captcha.Mundo("Somalia", "Mogadiscio"));
capitales.Add(new Captcha.Mundo("Yibuti", "Yibuti"));
capitales.Add(new Captcha.Mundo("Australia", "Camberra"));
capitales.Add(new Captcha.Mundo("Fiyi", "Suva"));
capitales.Add(new Captcha.Mundo("Samoa", "Apia"));
capitales.Add(new Captcha.Mundo("Nauru", "Yaren"));
this.Capitales = capitales;

SelCapital();
}

/// <summary>
/// Selecciona la capital a mostrar aleatoriamente
/// </summary>
private void SelCapital()
{
Random rnd = new Random();
pos = NumeroAleatorio(this.Capitales.Count);
lblSMS.Text = ((Mundo)Capitales[pos]).Pais;
txtValidacion.Text = "";
//almacenar los resultados
Session["CaptchaMundo"] = Capitales;
Session["CaptchaPos"] = pos;
}

/// <summary>
/// Obtener un numero aleatorio entre 0 y el maximo enviado
/// </summary>
/// <param name="max">Maximo numero aleatorio a calcular</param>
/// <returns></returns>
private int NumeroAleatorio(int max)
{
Random rnd = new Random();
return rnd.Next(0, max);
}
#endregion

protected void Page_Load(object sender, EventArgs e)
{
if (this.cssContenedor != null) Ventana.CssClass = this.cssContenedor;
if (this.cssSMS != null) lblSMS.CssClass = this.cssSMS;
if (this.cssTexto != null) txtValidacion.CssClass = this.cssTexto;
if (this.ColorFondoRespuesta != null) txtValidacion.BackColor = this.ColorFondoRespuesta;
else this.ColorFondoRespuesta = System.Drawing.Color.Transparent;

if (!Page.IsPostBack)
{
txtValidacion.BackColor = this.ColorFondoRespuesta;
lblSMS.Attributes["for"] = txtValidacion.ClientID;
txtValidacion.Attributes["placeholder"] = "?";
txtValidacion.BackColor = System.Drawing.Color.Transparent;
switch (estilo)
{
case Estilos.Numerico:
CrearEcuacion();
if (this.WidthRespuesta == null) this.WidthRespuesta = new Unit("50px");
break;
case Estilos.Capitales:
if (this.WidthRespuesta == null) this.WidthRespuesta = new Unit("150px");
if (this.Capitales == null) CrearCapitales();
else SelCapital();
break;
default:
lblSMS.Text = "OH!OH!";
break;
}
txtValidacion.Width = this.WidthRespuesta;
}
else
{
switch (estilo)
{
case Estilos.Numerico:
try
{
lblSMS.Text = Session["CaptchaEcuacion"].ToString();
}
catch (Exception ex)
{
lblSMS.Text = ex.ToString();
CrearEcuacion();
}
break;
case Estilos.Capitales:
if (this.Capitales == null)
{
try
{
Capitales = (List<Mundo>)Session["CaptchaMundo"];
}
catch
{
CrearCapitales();
}
try
{
pos = (int)Session["CaptchaPos"];
}
catch
{
pos = -1;
}
}
else
{
SelCapital();
}
break;
default:
lblSMS.Text = "OH!OH!";
break;
}
}
}

/// <summary>
/// Definicion de las capitales del mundo
/// </summary>
public class Mundo
{
public string Pais;
public string Capital;

public Mundo(string pais, string capital)
{
this.Pais = pais;
this.Capital = capital;
}
}
}

Happy Codding

#CSHARP #trucos #ASP #web #UserControl

0 Comentarios

Escribir un comentario

8 x 2 =



Archivo