65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 VB.NET 的 ASPX 验证码

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (10投票s)

2011年1月4日

CPOL

3分钟阅读

viewsIcon

58556

为 aspx 网页生成验证码图像。

引言

为了减少垃圾邮件的发生率,并降低拒绝服务攻击的可能性,许多网页都包含一个带有扭曲文字的图像块,称为验证码。(在 Wikipedia 上有一篇很好的文章,网址为 http://en.wikipedia.org/wiki/CAPTCHA。)

Code Project 和其他开源资源都有一些用于在网页上生成验证码图像的代码片段。但是,大多数都是旧的,并使用中等复杂度的 JavaScript 和/或 C#。本文中的代码使用 Visual Basic 和 .NET Framework 中日益强大的功能来生成更复杂和更简单的代码。例如,过去将二进制数据转换为 base64 需要的 30 行代码现在可以用一行完成。

图像是通过从内存位图中获取图形对象来绘制的。同样,.NET Framework 简化了将位图转换为可以在网页上显示的格式的过程,使用 bitmap.save(stream, imageformat) 函数将图像写入内存流。然后读取内存流并将其转换为所需的 base64 字符串

验证码并不特别复杂。但是,很容易修改代码以获得,例如,更平滑的角度和大小变化,并在文本上绘制一条更具(人类)吸引力的干扰线。

Using the Code

该代码是一个简短的类,其中有一个生成假词的方法和一个生成带有扭曲文本的内存位图的第二种方法。生成这些之后,将假词放置在页面上的隐藏文本框中,并通过修改页面上放置的 <img /> 标签的 src 属性将图像添加到页面。

(我要感谢 www.tipstricks.org 提供生成假词的基础 - 用脚本编写,但现在已翻译成 VB.NET。RndInterval 函数可以更新,但这样做不值得)。

Imports System.Drawing
Public Class Captcha
Dim bmpCaptcha As Bitmap
Dim iBMPHeight As Integer = 50
Dim iBMPWidth As Integer = 220
Dim sLeftMargin As Single = 20
Dim sTopMargin As Single = 10
Dim g As Graphics
Dim sWord As String
Dim sLetter As String
Dim sfLetter As SizeF
Dim rfLetter As RectangleF
Dim sX1 As Single = 0
Dim sY1 As Single = 0
Dim sX2 As Single = 0
Dim sY2 As Single = 0
Dim sTemp As Single
Dim iAngle As Integer
Dim sOffset As Single

Public Function GenerateCaptcha(ByVal sWord As String) As Bitmap
Dim ixr As Integer
bmpCaptcha = New Bitmap(iBMPWidth, iBMPHeight, _
		Drawing.Imaging.PixelFormat.Format16bppRgb555)
g = Graphics.FromImage(bmpCaptcha)
Dim drawBackground As New SolidBrush(Color.Silver)
g.FillRectangle(drawBackground, New Rectangle(0, 0, iBMPWidth, iBMPHeight))

' Create font and brush.
Dim drawFont As New Font("Times New Roman", 20)
Dim drawBrush As New SolidBrush(Color.DarkBlue)
Dim strFormat As New StringFormat(StringFormatFlags.FitBlackBox)
sfLetter = New SizeF(30, 30)
' Draw string to screen.
For ixr = 0 To sWord.Length - 1
sLetter = sWord.Substring(ixr, 1)
g = Graphics.FromImage(bmpCaptcha)
rfLetter = New RectangleF(sLeftMargin, sTopMargin, 30, 30)
sfLetter = g.MeasureString(sLetter, drawFont, sfLetter, strFormat)
iAngle = RndInterval(0, 20) - 10
With rfLetter
sOffset = sLeftMargin * Math.Tan(iAngle * Math.PI / 180)
.Y = sTopMargin - sOffset
.Width = sfLetter.Width + 10
.Height = sfLetter.Height
End With
g.RotateTransform(iAngle)
g.DrawString(sLetter, drawFont, drawBrush, rfLetter)
sLeftMargin += sfLetter.Width + 2
Next
Dim drawPen As Pen = New Pen(Color.Crimson, 1)
For ixr = 0 To 3
sX1 = sX2
Do While Math.Abs(sX1 - sX2) < iBMPWidth * 0.5
sX1 = RndInterval(2, iBMPWidth - 2)
sX2 = RndInterval(2, iBMPWidth - 2)
Loop
sY1 = sY2
Do While Math.Abs(sY1 - sY2) < iBMPHeight * 0.5
sY1 = RndInterval(2, iBMPHeight - 2)
sY2 = RndInterval(2, iBMPHeight - 2)
Loop
If RndInterval(0, 2) > 1 Then
sTemp = sX1
sX1 = sX2
sX2 = sTemp
End If
If RndInterval(0, 2) > 1 Then
sTemp = sY1
sY1 = sY2
sY2 = sTemp
End If

g.DrawLine(drawPen, sX1, sY1, sX2, sY2)
Next
g.Dispose()
Return bmpCaptcha
End Function
Public Function GenerateFakeWord(ByVal iLengthRequired As Integer) As String
Dim sVowels As String = "AEIOU"
Dim sConsonants As String = "BCDFGHJKLMNPQRSTVWXYZ"
Dim iNbrVowels As Integer
Dim iNbrConsonants As Integer
Dim sWord As String
Dim bUseVowel As Boolean
Dim iWordLength As Integer
Dim sPattern As String
iNbrVowels = 0
iNbrConsonants = 0
bUseVowel = False
sWord = ""
Randomize()
For iWordLength = 1 To iLengthRequired
If (iWordLength = 2) Or ((iLengthRequired > 1) And (iWordLength = iLengthRequired)) Then
bUseVowel = Not bUseVowel
ElseIf (iNbrVowels < 2) And (iNbrConsonants < 2) Then
bUseVowel = ((Rnd(1) * 2) > 1)
ElseIf (iNbrVowels < 2) Then
bUseVowel = True
ElseIf (iNbrConsonants < 2) Then
bUseVowel = False
End If

sPattern = IIf(bUseVowel, sVowels, sConsonants)
sWord = sWord & sPattern.Substring(Int(Rnd(1) * sPattern.Length), 1)
If bUseVowel Then
iNbrVowels = iNbrVowels + 1
iNbrConsonants = 0
Else
iNbrVowels = 0
iNbrConsonants = iNbrConsonants + 1
End If
Next
Return sWord
End Function
Private Function RndInterval(ByVal iMin As Integer, ByVal iMax As Integer) As Integer
Randomize()
Return Int(((iMax - iMin + 1) * Rnd()) + iMin)
End Function
End Class

要使用类,请在 aspx 页面上包含类似以下三个标签的内容

<body onload="onload()">

<asp:Image ID="imgCaptcha" runat="server" style="z-index: 1; left: 570px; top: 220px; 
position: absolute; width: 220px; height: 50px;" /> 

<asp:TextBox ID="txtCaptcha" runat="server" 
style="z-index: 1; left: 67px; top: 474px; position: absolute" 
Visible="False">txtCaptcha</asp:TextBox>

最后,您需要在 aspx.vb 代码页面中添加一些代码来生成验证码图像并在网页上显示它。变量 bOutputCaptcha 可以设置为输出验证码图像,或者让页面 OnLoad 函数什么也不做并隐藏图像控件。通常,页面将在用户响应后重新加载,该响应可以与存储在隐藏的 textbox, txtCaptcha 中的单词进行比较。(据称,aspx 控件的视图状态足够安全,计算机不容易提取该值。但是,如果确实认为有必要,可以将该值保留在服务器端)。

Private Sub ForgottenPassword_PreRender_
	(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

        ' Example of inline base64 image tag: <img width="16" height="14" 
        ' alt="embedded folder icon" src="data:image/gif;base64,
        ' R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//
        ' mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcpp
        ' V0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAg
        ' VGoEGv2xsBxqNgYPj/gAwXEQA7" />

        Dim sWord As String
        Dim bmpCaptcha As Bitmap
        Dim sScript As String
        Dim sImage64 As String
        Dim iLength As Integer
        Dim iRead As Integer
        Dim msCaptcha As MemoryStream

        If bOutputCaptcha Then

              sWord = myCaptcha.GenerateFakeWord(6)
              txtCaptcha.Text = sWord

              bmpCaptcha = myCaptcha.GenerateCaptcha(sWord)

              '   The following commented out code can be used for testing
              '   It writes the image to a page by itself
             'Response.ContentType = "image/jpeg"
             'Response.AppendHeader("Content-Disposition", "inline; filename=captcha.bmp")
             'Response.CacheControl = "no-cache"
             'Response.AppendHeader("Pragma", "no-cache")
             'Response.Expires = -1
             'bmpCaptcha.Save(Response.OutputStream, Imaging.ImageFormat.Jpeg)
             'Response.Flush()

             msCaptcha = New MemoryStream
             bmpCaptcha.Save(msCaptcha, Imaging.ImageFormat.Jpeg)

             sImage64 = ""

             iLength = msCaptcha.Length
             Dim byteImage As Byte()

             byteImage = New Byte(CType(msCaptcha.Length, Integer)) {}

             msCaptcha.Position = 0
             iRead = msCaptcha.Read(byteImage, 0, iLength)
             sImage64 = Convert.ToBase64String(byteImage)

             sScript = vbCr + <script language="Javascript"> " vbCr

             sScript += "function onload() {" + vbCr
             sScript += "var image;" + vbCr
             sScript += "image=document.getElementById(""imgCaptcha"");" + vbCr
             sScript += "image.src='data:image/gif;base64," + sImage64 + "'"
            
             '   It would be possible to test for IE7 or earlier here and 
             '   put the captcha word into the alt text - but 
             '   does increase the chances of a denial of service attack 
             '   if someone figures this out
             sScript += "}" + vbCr + vbCr
             sScript+="</Script>" + vbCr

             Me.ClientScript.RegisterClientScriptBlock(Me.GetType(), quot;image", sScript)

             msCaptcha.Close()
             bmpCaptcha.Dispose()

        Else

             imgCaptcha.Visible = False
             sScript = vbCr + <script language="Javascript"> " vbCr
             sScript += "function onload() {" + vbCr
             sScript += "}" + vbCr + vbCr
             sScript+="</Script>" + vbCr

             Me.ClientScript.RegisterClientScriptBlock(Me.GetType(), "image", sScript)

        End If

    End Sub

注意被注释掉的一小段代码 - 可以将位图直接保存到 http 响应流中,并在页面上单独显示图像以进行测试。

用户需要在其浏览器上允许脚本。

图像使用内联 URI 进行图像显示。图像以 base64 编码(即 7 位字符)。这意味着图像通常比原始位图大 30%(在本例中通常约为 3KB)。

并非所有浏览器都会显示内联 base64 图像 - 最重要的是 Internet Explorer 7 及更早版本将不会显示它们。Internet Explorer 8 及更高版本将显示图像,并且限制为 32KB。据称,Firefox 和 Safari 这样做的时间更长,但许多都限制为 4KB 图像。此处生成的位图图像通常为 3KB,因此即使转换为 base64 后也应该能够显示。

(我希望脚本被准确翻译。CodeProject 网页认为它是恶意代码,并删除了 <script></script> 标签之间的所有内容 - 所以我无法直接复制它,不得不分别输入代码!!)

历史

  • 到目前为止(2011 年 1 月),代码未做任何更改。

' http://www.tipstricks.org/

© . All rights reserved.