做在线投票防止作弊的一些体会

前一阵子因为需要,做一个在线投票的程序,在程序发布以后,一直出现各种bug
一开始是用Cookies做验证,客户禁用Cookies作弊,后来采取记录IP,客户端用软件速度换IP
后来还发现一个问题,就是投票按钮可以重复提交,用户按住投票按钮不放,就可以一直刷票
最后,还是才用Cookies验证的方法
思路是这样的
首先在一个页面产生一个验证码,把它记录到一个Cookies里:Response.Cookies.Add(new HttpCookie("CheckCode", checkCode));
在投票按钮事件出发后,首先判断Response.Cookies["CheckCode"]是否==null,如果成立
则提示用户需开启Cookies才能投票,关闭投票小窗口
产生验证码的代码如下:
CheckCode.aspx.cs
private string GenerateCheckCode()
  {
   int number;
   char code;
   string checkCode = String.Empty;

   System.Random random = new Random();

   for(int i=0; i<5; i++)
   {
    number = random.Next();

    if(number % 2 == 0)
     code = (char)('0' + (char)(number % 10));
    else
     code = (char)('A' + (char)(number % 26));

    checkCode += code.ToString();
   }

   Response.Cookies.Add(new HttpCookie("CheckCode", checkCode));

   return checkCode;
  }

  private void CreateCheckCodeImage(string checkCode)
  {
   if(checkCode == null || checkCode.Trim() == String.Empty)
    return;

   System.Drawing.Bitmap image = new System.Drawing.Bitmap((int)Math.Ceiling((checkCode.Length * 12.5)), 22);
   Graphics g = Graphics.FromImage(image);

   try
   {
    //生成随机生成器
    Random random = new Random();

    //清空图片背景色
    g.Clear(Color.White);

    //画图片的背景噪音线
    for(int i=0; i<25; i++)
    {
     int x1 = random.Next(image.Width);
     int x2 = random.Next(image.Width);
     int y1 = random.Next(image.Height);
     int y2 = random.Next(image.Height);

     g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
    }

    Font font = new System.Drawing.Font("Arial", 12, (System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic));
    System.Drawing.Drawing2D.LinearGradientBrush brush = new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), Color.Blue, Color.DarkRed, 1.2f, true);
    g.DrawString(checkCode, font, brush, 2, 2);

    //画图片的前景噪音点
    for(int i=0; i<100; i++)
    {
     int x = random.Next(image.Width);
     int y = random.Next(image.Height);

     image.SetPixel(x, y, Color.FromArgb(random.Next()));
    }

    //画图片的边框线
    g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);

    System.IO.MemoryStream ms = new System.IO.MemoryStream();
    image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
    Response.ClearContent();
    Response.ContentType = "image/Gif";
    Response.BinaryWrite(ms.ToArray());
   }
   finally
   {
    g.Dispose();
    image.Dispose();
   }
  }
 }

在Vote.aspx里引用上页面所产生的验证码
在登录页面中使用“<IMG>” 这个 HTML 元素来显示生成的验证码图片:<IMG src="CheckCode.aspx">
投票按钮事件处理
vote.aspx.cs
private void Button1_Click(object sender, System.EventArgs e)
  {
   if(Request.Cookies["CheckCode"] == null)
   {
    Response.Write("<script language=\"JavaScript\">alert('您的浏览器设置已被禁用 Cookies,您必须设置浏览器允许使用 Cookies 选项后才能投票!');window.close();</script>");
    return;
   }
   Button1.Enabled = false;
   UpdateVote();
  }

private void UpdateVote()
  {
   if(String.Compare(Request.Cookies["CheckCode"].Value, txtValidate.Text, true) != 0)
   {
    Response.Write(HouseBasic.ScriptAlertMsg("验证码错误,请输入正确的验证码。"));
    return;
   }
   string vote = "," + Request.QueryString["id"];
   if(Request.Cookies["Vote"]   !=   null)    
   {  
    if(Request.Cookies["Vote"]["IP"] == Request.ServerVariables["REMOTE_ADDR"] && Request.Cookies["Vote"]["ID"].IndexOf(vote) >= 0)
    {
     Response.Write("<script language=\"JavaScript\">alert('您今天已经为本选手投了一票,不能再投了哟:');window.close();</script>");
     Response.End();
    }
    vote += "," + Request.Cookies["Vote"]["ID"];
   }  
   HttpCookie cookie = new HttpCookie("Vote");
   cookie.Values.Add("IP", Request.ServerVariables["REMOTE_ADDR"]);
   cookie.Values.Add("ID", vote);
   cookie.Expires = DateTime.Now.AddDays(1);
   Response.Cookies.Add(cookie);
   //投票处理
}
现在还剩下防止Button按钮重复提交了
在网上搜索了一个防止重复提交的js方法
js.js
function _doPostBack(){};
if(typeof("__doPostBack")=="function")
{
 _doPostBack=__doPostBack;
 __doPostBack=_doPostBackNew;
}

document.attachEvent("onmousemove",_onmousemove);
var _isPosting=false;
var _divMask=null;

function _onmousemove()
{
 if(_divMask)
 with(_divMask.runtimeStyle)
 {
  left=event.clientX+document.body.scrollLeft-4;
  top=event.clientY+document.body.scrollTop-4;
 }
}


function _makeMask()
{
 var div=document.createElement("DIV");
 with(div.runtimeStyle)
 {
  position="absolute";
  zIndex=999999;
  fontSize="1px";
  left=event.clientX+document.body.scrollLeft-4;
  top=event.clientY+document.body.scrollTop-4;
  width="8px";
  height="8px";
  cursor="wait";
 
  backgroundColor="gray";
  filter="alpha(opacity=10)";
 }
 try
 {
  document.body.insertAdjacentElement("BeforeEnd",div);
  div.onblur=new Function("this.focus()");
  div.focus();
 }
 catch(x){}
 
 if(_divMask)_divMask.removeNode(true);
 _divMask=div;
}

function _doPostBackNew(sender,args)
{
 if(_isPosting)
  return event.returnValue=!(event.cancelBubble=true);

 status="正在更新页面...";
 _doPostBack(sender,args); 
 _isPosting=true;
 _makeMask();
}

function _onformsubmit()
{
 if(_isPosting)
  return event.returnValue=!(event.cancelBubble=true);

 _isPosting=true;
 _makeMask();
}
new function _attachForms()
{
 with(new Enumerator(document.forms))
 for(;!atEnd();moveNext())
 {
  item().attachEvent("onsubmit",_onformsubmit);
  var div=document.createElement("div");
  div.runtimeStyle.width="0px";
  div.runtimeStyle.hight="0px";
  div.runtimeStyle.overflow="hidden";
  div.runtimeStyle.position="absolute";
  item(0).insertAdjacentElement("afterbegin",div);
  div.innerHTML="<INPUT TYPE=Submit name='webformpatchsubmitelement' onclick='return event.returnValue=false' id='webformpatchsubmitelement' value='webformpatchsubmitelement'/>";
 }
}
把这个作为 <-script src=js.js-><-/script-> 的形式Render到每个页面中就可以了。|
如果有PageBase,则在Init的时候用RegisterClientScriptBlock放上去更好。
如:this.RegisterStartupScript("JS","<script src=\"js.js\" type=\"text/javascript\"></script>");

我的这个投票基本上处理,能防止一些常见的作弊方法,欢迎大家提出不足之处   
和漏洞

posted @ 2006-10-27 10:27  拖拉机大叔  阅读(1251)  评论(2编辑  收藏  举报