登陆
浏览模式: 标准 | 列表 分类: ASP

ASP 对提交做限制

感谢奇奇的代码
<%
    If Request.cookies("issend")="yes" Then
    Response.write "<script>alert('一天只能提交一次')</script>"
    Response.End
    Else
    Set rs=Server.CreateObject("ADODB.Recordset")
                ……
           ……
           ……
    rs.close
    set rs=Nothing
    Response.Cookies("issend")="yes"
    Response.Cookies("issend").Expires = DateAdd("d",1,Now)
    End if
%>

Tags: ASP

asp验证码bmp图片的生成原理

(不必知道BMP的二进制构成)

<%
'------ access911.net 开发验证码图片 --------
'以下是方法原理演示,并没有做最优化,请大家自己看吧,解释绝对够详细了,
'你不需要有 BMP 图片的知识都可以看懂

'如果你要自定义图片,只要在MYCODE中建立对应的文件夹,比如 mycode\w15h15;
'然后在其中用 WINDOWS 自己带的画图软件建立11张BMP图片,
'按规定定义图片名称即可,0-9 可定义为 0.BMP .... 9.BMP;
'最终要显示在客户端界面的 BMP 图片的大小应该定义为 displaysize.bmp;
'注意 0-9.BMP 与 DISPLAYSIZE.BMP 的高度和宽度要配合
'displaysize.BMP的底色就是杂点的颜色


Option Explicit
Response.buffer=true
NumCode

Function NumCode()
    Response.Expires = -1
    Response.AddHeader "Pragma","no-cache"
    Response.AddHeader "cache-ctrol","no-cache"
    On Error Resume Next
    Dim strNumber,i,j
    Dim adoStream,adoStream1,adoStream2
    dim CodeLen
    dim fld
    Dim intImg(),strAll
    dim lngZ            '杂点的百分比
    dim x1,x2,x3(2)
    Dim Pos                '文件头的长度,一般固定为 54
    dim w,h,w1,w0,h0

    pos=54                '文件头的长度,一般固定为 54
    lngZ=2                '杂点比率是 1%
    '对应文件的目录,长宽不同的BMP图片放在不同目录
    fld = "w30h20"        'w宽度,h高度
    
    
    Set adoStream=Server.CreateObject("Adodb.Stream")
    adoStream.Mode=3
    adoStream.Type=1
    adoStream.Open
    Set adoStream1=Server.CreateObject("Adodb.Stream")
    adoStream1.Mode=3
    adoStream1.Type=1
    adoStream1.Open
    Set adoStream2=Server.CreateObject("Adodb.Stream")
    adoStream2.Mode=3
    adoStream2.Type=1
    adoStream2.Open
    
    adoStream2.LoadFromFile(Server.mappath("mycode/" &amp; fld &amp; "/displaysize.bmp"))
    if err<>0 then
        response.Write server.HTMLEncode(err.number &amp; err.Description)
        response.End
    end if
    
    'displaysize.bmp 是你自己定义的显示的 BMP 的大小,我们只要取他的前54个字节
    'adoStream2中存储了头字节

    'bmp图像头信息中,&amp;H12 的四个字节代表宽  &amp;H16 的四个字节代表高
    adoStream2.position= &amp;H12        '3+15 18个字节开始
    w=adoStream2.read(4)            '宽
    h=adoStream2.read(4)            '高
    w=binval(w)                        '显示时候的总宽度
    h=binval(h)
    adoStream2.Position =0

    adoStream.LoadFromFile(Server.mappath("mycode/" &amp; fld &amp; "/0.bmp"))
    if err<>0 then
        response.Write server.HTMLEncode(err.number &amp; err.Description)
        response.End
    end if
    adoStream.position = &amp;H12
    w0=adoStream.read(4)            '宽
    h0=adoStream.read(4)            '高
    w0=binval(w0)                    '显示时候的总宽度
    h0=binval(h0)

    '显示验证码图片的总宽度是每个图片的公倍数,并且显示验证码图片高度与每个数字图片高度一致
    if w mod w0 = 0 and h = h0 then
        '验证码由 N 个字构成,可更改,但是注意要与 displaysize.bmp配合
        CodeLen= clng(w/w0)
    else
        response.write "显示图片宽度与数字图片宽度不符合或高度不符,请用画图软件重新编辑"
        response.end
    end if

    w1=clng(w/CodeLen)                '每个图片的宽度

    Randomize timer
    'strNumber = clng(8999*Rnd+1000)
    '指定范围取随机整数 Int((upperbound - lowerbound + 1) * RND + lowerbound)
    for i=1 to CodeLen
        strNumber=strNumber &amp; cstr(Int((9 - 0 + 1) * RND + 0))
    next
    Session("GetCode") = left(strNumber,CodeLen)
    strAll=strNumber
    redim intImg(CodeLen)
    For i=0 To CodeLen-1
        '以下代码用于调试,可以删除
        'response.write "d" &amp; i &amp; "d" &amp; mid(strAll,i+1,1) &amp; "<br>"
        intImg(i)=cint(mid(strAll,i+1,1))
    Next

    for i=0 to CodeLen-1
        adoStream.LoadFromFile(Server.mappath("mycode/" &amp; fld &amp; "/" &amp; intImg(i) &amp;".bmp"))
        '以下代码用于调试,可以删除
        'adostream.Position =0
        'response.Write lenb(adostream.Read())
        'response.End
        '调试结束
        
        '开始做杂点
        for x2=1 to w1*h/100*lngZ
            Randomize timer
            x1=clng(clng((w1*h) - 0 + 1) * RND + 0)    '随机取某个像素
            if (pos+x1*3) < (w1*h*3+pos) then
                adoStream.Position = (pos + x1*3)
            end if
            '注意,displaysize.BMP的底色就是杂点的颜色
            adoStream2.Position =54
            adoStream.Write adoStream2.Read(3)
        next
        '杂点结束
    
        adoStream.position=54                    '偏移到第54个字节,也就是图像矩阵开始的地方
        adoStream1.write adoStream.read()        '读取第54个字节到最后
    next
    '现在为止, adoStream1中存储了所有图像,不过是竖的
    adostream1.Position =0
    '以下代码用于调试,可以删除
    'response.Write LENB(adostream1.read()) &amp; "<BR>"
    'response.End
    '调试结束

    adoStream2.Position =0

    '这里要进行行列转换
    '如果是20像素*20像素,竖排,每像素1200/20/20=3个字节,每行20*3=60字节
    DIM x,t1,t2,t3
    For i=0 To h-1 Step 1    '高度是几个像素
        For j=0 To CodeLen-1    'j代表有几个验证码字符
            '如果是20*20像素,第一个图片的第一行,就应该停在0,第二个图片应该停在 1200字节上,第二行第一个图片时应该,60,第二行第二个图片应该是J*1200+i*60
            
            'adoStream1.Position=i*60+j*1200                        '固定大小:20*20
            'adoStream1.Position=i*60+j*1800                        '固定大小:20*20
            'adoStream2.Position=Pos+60*j+i*360                        '固定大小:20*20
            'adoStream2.write adoStream1.read(60)                    '固定大小:20*20

            'adoStream1.Position=i*w1*3+j*w1*h*3
            '由于扫描时,每一行必须是4的公倍数,因此上面这行由下面这段 IF 代替
            IF (w1*3) MOD 4 <>0 THEN
                FOR X = 1 TO 20
                    if (w1*3+X) MOD 4 = 0 then
                        exit for
                    end if
                NEXT
                t1=i*(w1*3+x)
                t2=j*(w1*3+x)*h
                '以下代码用于调试,可以删除
                'RESPONSE.Write "I:" &amp; i &amp; " J:" &amp; J &amp; " W1:" &amp; w1 &amp; "--" &amp; T2+T1 &amp; "<BR>"
                adoStream1.Position =t1+t2
                '以下这句被前面那句 T1+T2 代替
                'adoStream1.Position=i*(w/CodeLen*3+x)+j*(w/CodeLen*3+x)*h
            ELSE
                '动态:w/CodeLen 是宽度,w/CodeLen*h 是整个图片的大小,每过一个J,就是条转到下一个图片的相同位置
                adoStream1.Position=i*w1*3+j*w1*h*3
            END IF
            
            '动态:w/CodeLen*3 是宽度,高度是第I行的整个字节长度 w/CodeLen*3*4
            'adoStream2.Position=Pos+w1*3*j+i*w*3                
            '由于扫描时,每一行必须是4的公倍数,因此上面这行由下面这段 IF 代替
            if (w*3) mod 4 <> 0 then
                FOR X = 1 TO 20
                    if (w*3+x) MOD 4 = 0 then
                        exit for
                    end if
                NEXT
                adoStream2.Position=Pos+w1*3*j+i*(w*3+x)
            else
                '动态:w/CodeLen*3 是宽度,高度是第I行的整个字节长度 w/CodeLen*3*4
                adoStream2.Position=Pos+w1*3*j+i*w*3                
            end if
            '动态:
            adoStream2.write adoStream1.read(w1*3)
        Next
    Next

    Response.ContentType = "image/BMP"
    adoStream2.Position=0
    '这里是组成好整个 BMP 以后再输出,你完全可以一面组织一面输出给客户端。
    '杂点也可以直接在输出的时候完成。
    Response.BinaryWrite adoStream2.read()
    
    adoStream.Close
    adoStream1.Close
    adoStream2.Close
    set adoStream=nothing
    set adoStream1=nothing
    set adoStream2=nothing
    
    If Err Then Session("GetCode") = err.description &amp; 9999
End Function

Public Function BinVal(byval Bin)
    Dim Ret, I
    Ret = 0
    For I = LenB(Bin) To 1 Step -1
        Ret = Ret * 256 + AscB(MidB(Bin, I, 1))
    Next
    BinVal = Ret
End Function

'------ access911.net 测试用 --------
%>

在ASP中限制同一表单被多次提交

   本文介绍在ASP应用中防止用户在当前会话期间多次提交同一表单的一个简单方法。它主要由四个子程序组成,在较为简单的应用场合,你只要将这些代码放在包含文件中直接引用即可;对于那些较为复杂的环境,我们在文章的最后给出一些改进建议。

   一、基本工作过程

   下面我们依次讨论这四个子程序。



   (一)初始化

   这里我们要在Session对象中保存两个变量,其中:

   ⑴ 每一个表单对应一个称为FID的唯一标识,为使该值唯一要用到一个计数器。

   ⑵ 每当一个表单成功提交,必须在一个Dictionary对象中存储它的FID。

   我们用一个专用的过程来初始化上述数据。虽然以后各个子程序都要调用它,但实际上每一个会话期间它只执行一次:

Sub InitializeFID()
If Not IsObject(Session("FIDList")) Then
Set Session("FIDList")=Server.CreateObject("Scripting.Dictionary")
Session("FID")=0
End If
End Sub



   (二)生成表单的唯一标识符

   下面这个函数GenerateFID()用于生成表单的唯一标志。该函数首先将FID值加1,然后返回它:


Function GenerateFID()
InitializeFID
Session("FID") = Session("FID") + 1
GenerateFID = Session("FID")
End Function



   (三)登记已提交表单

   当表单成功地提交时,在Dictionary对象中登记它的唯一标识:


Sub RegisterFID()
Dim strFID
InitializeFID
strFID = Request("FID")
Session("FIDlist").Add strFID, now()
End Sub


   (四)检查表单是否重复提交

   在正式处理用户提交的表单之前,应该在Dictionary对象中检查它的FID是否已经登记。下面的CheckFID()函数用来完成这个工作,如已经登记,它返回FALSE,否则返回TRUE:

Function CheckFID()
Dim strFID
InitializeFID
strFID = Request("FID")
CheckFID = not Session("FIDlist").Exists(strFID)
End Function


   二、如何使用

   有两个地方要用到上述函数,即表单生成时与结果处理时。假设上述四个子程序已经放入包含文件Forms.inc中,下面的代码根据FID值来决定生成表单还是处理表单结果,它所描述的处理过程适合于大多数ASP应用:


< %Option Explicit%>
< !--#include file="forms.inc"-->
< HTML>
< HEAD>
< TITLE>表单提交测试< /TITLE>
< /HEAD
< BODY>
< %
If Request("FID") = "" Then
GenerateForm
Else
ProcessForm
End If
%>
< /BODY>
< /HTML>


   GenerateForm负责生成表单,表单中应该含有一个隐藏的FID,如:

< %
Sub GenerateForm()
%>
< form action="< %=Request.ServerVariables("PATH_INFO")%>" method=GET>
< input type=hidden name=FID value="< %=GenerateFID()%>">
< input type=text name="param1" value="">
< input type=submit value="OK">
< /form>
< %
End Sub
%>


   ProcessForm负责处理通过表单提交的内容,但在处理之前应该先调用CheckFID()检查当前表单是否已经提交,代码类如:


< %
Sub ProcessForm()
If CheckFID() Then
Response.Write "你输入的内容是" &amp; Request.QueryString("param1")
RegisterFID
Else
Response.Write "此表单只能提交一次!"
End If
End Sub
%>


   三、限制与改进措施

   上面我们介绍了在当前会话期间限制同一表单被多次提交的一种方法。在实际应用中可能需要从多方面加以改进,例如:

   ⑴ 在登记表单ID之前检查用户输入数据的合法性,使得数据不合法时用户可以按“后退”按钮返回,在修正后再次提交同一表单。

   ⑵ 这种对表单提交的限制最多只能在当前会话期间有效。如果要求这种限制能够跨越多个会话,那么就要用到Cookeis或数据库来保存相关数据了。

   ⑶ 这种方法是不安全的。它仅用于防范误操作,不能防止熟练用户有意地多次提交同一表单。

Tags: ASP, 限制多次提交

Total:53‹ Prev123456