做在线投票防止作弊的一些体会
前一阵子因为需要,做一个在线投票的程序,在程序发布以后,一直出现各种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>");
我的这个投票基本上处理,能防止一些常见的作弊方法,欢迎大家提出不足之处
和漏洞