数字式验证码
<%
option explicit
'----------------------------------------------------------------------------------
'数字式验证码
'copyright apex, wanbo,tang 2009, 7. msn: apex-wanbo@hotmail.com
'----------------------------------------------------------------------------------
class Captcha
'字符集
private charSet
'字符位图数据
private bmpChars
'位图颜色代码
private bmpColors
'字符的宽度和高度
private charWidth, charHeight
'图像的宽度和高度
private imageWidth, imageHeight
private mvarCodeLength
private mvarAllowRepeat
private mvarRandomColor
private mvarRandomRotate
private mvarSessionName
private mvarNoiseLine
'验证码的长度(默认为 6)
property let CodeLength(value)
'使用 4-8 位字符
if (value >= 4 and value <= 8) then mvarCodeLength = value
end property
property get CodeLength()
CodeLength = mvarCodeLength
end property
'是否允许相同的字符(默认为 true)
property let AllowRepeat(value)
if value = true or value = false then mvarAllowRepeat = value
end property
property get AllowRepeat()
AllowRepeat = mvarAllowRepeat
end property
'是否使用随机颜色(默认为 true)
property let RandomColor(value)
if value = true or value = false then mvarRandomColor = value
end property
property get RandomColor()
RandomColor = mvarRandomColor
end property
'旋转位图(默认为 true)
property let RandomRotate(value)
if value = true or value = false then mvarRandomRotate = value
end property
property get RandomRotate
RandomRotate = mvarRandomRotate
end property
'用于 Session 的名称(默认为 "CaptchaCode")
property let SessionName(value)
if trim(value) <> "" then mvarSessionName = value
end property
property get SessionName
SessionName = mvarSessionName
end property
'干扰线的数量(默认为 6)
property let NoiseLine(value)
'0-10 之间,太多则使文本变得不易辨认
if value >= 0 and value <= 10 then mvarNoiseLine = value
end property
property get NoiseLine
NoiseLine = mvarNoiseLine
end property
'类初始化代码
private sub Class_Initialize()
'使用 0-9 数字
charSet = "0123456789"
'按字符集的顺序加载字符位图数据
bmpChars = Array( _
Split("0,0,4,2,5,1,5,6,4,0,0,0,4,7,1,1,1,1,1,7,4,0,0,6,1,1,3,0,3,5,1,6,0," & _
"4,1,1,3,0,0,0,3,1,1,4,8,1,7,0,0,0,0,0,7,1,8,6,1,9,0,0,0,0,0,9,1,6," & _
"10,1,11,0,0,0,0,0,2,1,10,1,1,3,0,0,0,0,0,12,1,5,1,1,3,0,0,0,0,0,3,1,1," & _
"1,1,3,0,0,0,0,0,3,1,1,7,1,8,0,0,0,0,0,3,1,1,10,1,2,0,0,0,0,0,8,1,7," & _
"9,1,6,0,0,0,0,0,2,1,10,8,1,5,0,0,0,0,0,13,1,2,4,1,1,3,0,0,0,4,1,1,14," & _
"0,2,1,1,3,0,15,7,1,10,0,0,0,10,1,1,1,1,1,1,15,0,0,0,0,11,7,1,1,6,4,0,0", ","), _
Split("3,1,1,1,1,1,1,1,1,1,2,3,1,1,1,1,1,1,1,1,1,2,0,0,0,0,2,1,1,0,0,0,0," & _
"0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,2,1,1,0,0,0,0," & _
"0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,2,1,1,0,0,0,0," & _
"0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,2,1,1,0,0,0,0," & _
"0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,2,1,1,0,0,0,0," & _
"0,0,0,0,2,1,1,0,0,0,0,2,1,1,1,1,1,1,0,0,0,0,2,1,1,1,1,1,1,0,0,0,0", ","), _
Split("1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,13,1,6,3,3,3,3,3,3,3,3," & _
"8,1,5,0,0,0,0,0,0,0,0,4,5,1,6,0,0,0,0,0,0,0,0,3,1,1,6,4,0,0,0,0,0," & _
"0,0,8,1,1,5,14,0,0,0,0,0,0,0,12,1,1,1,11,0,0,0,0,0,0,0,14,7,1,1,6,0,0," & _
"0,0,0,0,0,4,6,1,1,6,0,0,0,0,0,0,0,0,6,1,1,12,0,0,0,0,0,0,0,0,5,1,13," & _
"0,0,0,0,0,0,0,0,2,1,1,3,13,0,0,0,0,0,0,2,1,1,1,1,11,0,0,0,0,0,10,1,7," & _
"11,1,1,9,14,0,15,6,1,1,12,0,6,1,1,1,1,1,1,1,9,0,0,0,3,13,5,1,7,6,3,0,0", ","), _
Split("1,1,5,10,6,8,4,0,0,0,0,1,1,1,1,1,1,1,2,4,0,0,0,0,0,3,11,10,1,1,1,14,0," & _
"0,0,0,0,0,0,3,5,1,1,15,0,0,0,0,0,0,0,15,1,1,6,0,0,0,0,0,0,0,0,9,1,1," & _
"0,0,0,0,0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,10,1,13,0,0,3,1,6,2,2,5,1,1,14," & _
"0,0,8,1,1,1,1,1,6,15,0,0,0,0,4,2,1,1,2,0,0,0,0,0,0,0,0,4,7,1,10,0,0," & _
"0,0,0,0,0,0,4,7,1,2,0,15,4,0,0,0,0,0,12,1,5,0,10,7,14,0,0,0,0,12,1,1,0," & _
"1,1,1,8,0,0,15,7,1,10,0,15,7,1,1,1,1,1,1,1,3,0,0,0,8,13,1,1,1,13,3,0,0", ","), _
Split("0,0,0,0,0,0,3,1,1,0,0,0,0,0,0,0,0,3,1,1,0,0,0,0,0,0,0,0,3,1,1,0,0," & _
"0,0,0,0,0,0,3,1,1,0,0,0,0,0,0,0,0,3,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1," & _
"1,1,1,1,1,1,1,1,1,1,1,1,5,0,0,0,0,3,1,1,0,0,2,1,8,0,0,0,3,1,1,0,0," & _
"4,1,5,0,0,0,3,1,1,0,0,0,2,1,8,0,0,3,1,1,0,0,0,4,1,5,0,0,3,1,1,0,0," & _
"0,0,2,1,8,0,3,1,1,0,0,0,0,4,1,5,0,3,1,1,0,0,0,0,0,2,1,8,3,1,1,0,0," & _
"0,0,0,4,1,5,3,1,1,0,0,0,0,0,0,2,1,13,1,1,0,0,0,0,0,0,4,1,1,1,1,0,0", ","), _
Split("1,1,1,1,5,10,11,4,0,0,0,1,1,1,1,1,1,1,1,2,0,0,11,3,0,0,15,8,5,1,1,9,0," & _
"0,0,0,0,0,0,0,6,1,1,3,0,0,0,0,0,0,0,0,5,1,13,0,0,0,0,0,0,0,0,9,1,1," & _
"0,0,0,0,0,0,0,0,2,1,1,0,0,0,0,0,0,0,0,9,1,1,15,3,15,0,0,0,0,4,5,1,13," & _
"8,1,1,12,0,0,14,7,1,1,14,3,1,1,1,1,1,1,1,1,8,0,14,1,5,13,1,1,5,6,15,0,0," & _
"0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,7,1,4,0,0,0,0,0,0,0," & _
"0,10,1,11,3,3,3,3,3,3,0,0,13,1,1,1,1,1,1,1,1,0,0,2,1,1,1,1,1,1,1,1,0", ","), _
Split("0,0,15,13,1,1,1,6,14,0,0,0,12,1,1,1,1,1,1,1,8,0,4,1,1,13,15,0,4,6,1,1,15," & _
"2,1,5,4,0,0,0,0,5,1,6,7,1,9,0,0,0,0,0,9,1,5,1,1,2,0,0,0,0,0,2,1,1," & _
"1,1,6,0,0,0,0,0,9,1,1,1,1,1,4,0,0,0,4,1,1,13,7,1,1,7,15,0,15,7,1,1,3," & _
"13,1,1,1,1,1,1,1,1,8,0,11,1,7,8,5,1,1,13,3,0,0,14,1,1,15,0,0,0,0,0,0,0," & _
"0,7,1,2,0,0,0,0,0,0,0,0,8,1,1,4,0,0,0,0,0,0,0,0,5,1,2,0,0,0,0,0,0," & _
"0,0,12,1,1,14,0,0,0,0,0,0,0,0,10,1,5,4,0,0,0,0,0,0,0,15,1,1,10,4,0,0,0", ","), _
Split("0,0,9,1,1,14,0,0,0,0,0,0,0,12,1,1,11,0,0,0,0,0,0,0,0,1,1,13,0,0,0,0,0," & _
"0,0,0,6,1,1,0,0,0,0,0,0,0,0,8,1,1,3,0,0,0,0,0,0,0,4,1,1,9,0,0,0,0," & _
"0,0,0,0,6,1,7,0,0,0,0,0,0,0,0,12,1,1,15,0,0,0,0,0,0,0,0,5,1,11,0,0,0," & _
"0,0,0,0,0,2,1,7,0,0,0,0,0,0,0,0,15,1,1,14,0,0,0,0,0,0,0,0,13,1,6,0,0," & _
"0,0,0,0,0,0,12,1,1,4,0,0,0,0,0,0,0,0,5,1,8,0,0,0,0,0,0,0,0,8,1,7,0," & _
"3,3,3,3,3,3,3,12,1,1,3,1,1,1,1,1,1,1,1,1,1,10,1,1,1,1,1,1,1,1,1,1,1", ","), _
Split("0,0,3,6,5,1,10,6,14,0,0,0,9,1,1,1,10,1,1,1,11,0,3,1,1,6,0,0,4,13,1,1,3," & _
"13,1,5,0,0,0,0,0,5,1,13,1,1,9,0,0,0,0,0,9,1,1,1,1,2,0,0,0,0,0,2,1,1," & _
"5,1,2,0,0,0,0,0,2,1,5,6,1,7,0,0,0,0,0,7,1,6,14,1,1,8,0,0,0,8,1,1,15," & _
"0,14,1,1,13,2,10,1,5,14,0,0,15,13,1,1,1,1,1,2,15,0,14,1,1,2,4,0,4,2,1,1,14," & _
"13,1,9,0,0,0,0,0,6,1,13,1,1,3,0,0,0,0,0,3,1,1,5,1,2,0,0,0,0,0,2,1,7," & _
"2,1,1,11,0,0,0,11,1,1,2,4,7,1,1,1,10,1,1,1,13,0,0,0,8,10,1,1,7,13,8,0,0", ","), _
Split("0,0,0,4,10,1,1,15,0,0,0,0,0,0,0,4,5,1,10,0,0,0,0,0,0,0,0,14,1,1,12,0,0," & _
"0,0,0,0,0,0,2,1,5,0,0,0,0,0,0,0,0,4,1,1,8,0,0,0,0,0,0,0,0,2,1,10,0," & _
"0,0,0,0,0,0,0,15,1,1,15,0,0,14,6,5,1,10,12,7,1,11,0,8,1,1,1,1,1,1,1,1,13," & _
"15,1,1,7,14,0,14,7,1,1,7,6,1,1,4,0,0,0,4,1,1,1,5,1,9,0,0,0,0,0,6,1,1," & _
"1,1,2,0,0,0,0,0,2,1,1,5,1,2,0,0,0,0,0,6,1,10,6,1,5,0,0,0,0,4,1,1,2," & _
"15,1,1,13,15,0,15,10,1,1,4,0,8,1,1,1,1,1,1,1,14,0,0,0,14,6,1,1,1,6,15,0,0", ","))
'位图颜色代码
bmpColors = Array( _
Split("00,01,02,03,04,05,06,07,08,09,0a,0b,0c,0d,0e,0f,10", ","), _
Split("00,11,12,13,14,15,16,17,18,19,1a,1b,1c,1d,1e,1f,20", ","), _
Split("00,21,22,23,24,25,26,27,28,29,2a,2b,2c,2d,2e,2f,30", ","))
'字符宽和高,所有字符使用同一大小
charWidth = 11
charHeight = 18
'位图的宽度和高度
imageWidth = 196
imageHeight = 48
'初始化属性
mvarCodeLength = 6
mvarAllowRepeat = true
mvarRandomColor = true
mvarRandomRotate = true
mvarSessionName = "CaptchaCode"
mvarNoiseLine = 6
end sub
'获取范围内的随机数
'lowBound-下限(较小的数);upperBound-上限(较大的数)
private function RandomInterval(lowBound,upperBound)
Randomize
RandomInterval = Int(((upperBound - lowBound + 1) * Rnd()) + lowBound)
end function
'位图调色板
private function GetPalette()
'预定义 BGRA 代码
const backColor = "ffffff00"
const redColors = "33006600c2b2d100e0d9e800f3f0f600714d9400ae9ac2008668a400d2c7dd00b8a7ca00967cb000cabdd700d9d0e300a38cba00e7e1ed00ede9f1006699ff00"
const greenColors = "00663300b2d1c200d9e8e000f0f6f3004d9471009ac2ae0068a48600c7ddd200a7cab8007cb09600bdd7ca00d0e3d9008cbaa300e1ede700e9f1ed0099ff6600"
const blueColors = "66330000d1c2b200e8e0d900f6f3f00094714d00c2ae9a00a4866800ddd2c700cab8a700b0967c00d7cabd00e3d9d000baa38c00ede7e100f1ede900ff669900"
'根据 "RandomColor" 属性返回调色板数量
if mvarRandomColor = true then
GetPalette = backColor & redColors & greenColors & blueColors
else
GetPalette = backColor & redColors
end if
end function
'位图颜色代码
private function GetColors()
if mvarRandomColor = true then
GetColors = bmpColors(RandomInterval(0, 2))
else
GetColors = bmpColors(0)
end if
end function
'格式化字符位图数据
'char-要获取位图数据的字符
private function GetFormatedCharbmp(char)
dim i, j
dim tmpArray, charbmp()
dim charColors
'在字符集中搜索字符的位置
i = InStr(charSet, char)
'如果字符包含在内,则格式化位图数据
if i > 0 then
tmpArray = bmpChars(i - 1)
redim charbmp(charHeight - 1, charWidth - 1)
'对位图进行着色
charColors = GetColors()
for i = 0 to charHeight - 1
for j = 0 to charWidth - 1
charbmp(i, j) = charColors(CInt(tmpArray(i * charWidth + j)))
next
next
GetFormatedCharbmp = charbmp
else
GetFormatedCharbmp = Array(0)
end if
end function
'随机验证码
private function GetCaptchaCode()
dim i, captchaCode, captchaChar
captchaCode = ""
captchaChar = ""
Randomize
for i = 1 to mvarCodeLength
do
captchaChar = Mid(charSet, Int(Len(charSet) * Rnd()) + 1, 1)
loop while not mvarAllowRepeat and CStr(captchaChar) = CStr(Right(captchaCode, 1))
captchaCode = captchaCode & captchaChar
next
GetCaptchaCode = captchaCode
end function
'格式化 16 进制字符串
'hexString-需要格式化的 16 进制字符串;fixbyte-格式化后的字节;reverse-是否翻转
private function GetFormatedHexString(hexString, fixbyte, reverse)
dim fixLength
dim tmpString, i
'一个字节等于两个 16 进制位
fixLength = fixbyte * 2
if fixLength > Len(hexString) then
hexString = String(fixLength - Len(hexString), "0") & hexString
end if
'翻转(相对于小端存储方式)
if reverse = true then
tmpString = ""
for i = 1 to Len(hexString) step 2
tmpString = Mid(hexString, i, 2) & tmpString
next
GetFormatedHexString = tmpString
else
GetFormatedHexString = CStr(hexString)
end if
end function
'写入 16 进制文件
private sub WriteHex(hexString)
dim i
for i = 1 to len(hexString) step 2
Response.BinaryWrite ChrB(CByte("&H" & Mid(hexString, i, 2)))
next
end sub
'旋转位图
'srcbmp-原位图;dstw,dsth-旋转所用画布大小;angle-旋转角度(以逆时针为正)
private function Rotate(srcbmp(), dstw, dsth, angle)
'圆周率
const pi = 3.1415927
'if angle mod 360 = 0 then
' Rotate = srcbmp
' exit function
'end if
dim srcw, srch
dim t, sinA, cosA
dim dstbmp(), backColor
dim dstx, dsty, srcx, srcy, x1, x2, y1, y2
'获取原图大小
srch = ubound(srcbmp, 1) + 1
srcw = ubound(srcbmp, 2) + 1
'转换为弧度
t = angle * pi / 180
sinA = sin(t)
cosA = cos(t)
redim dstbmp(dsth - 1, dstw - 1)
'获取背景
backColor = GetColors()(0)
'复制像素(使用中心为辅助点)
for dstx = 0 to dstw - 1
x1 = dstx - dstw * 0.5
for dsty = 0 to dsth - 1
y1 = dsty - dsth * 0.5
x2 = x1 * cosA - y1 * sinA
y2 = y1 * cosA + x1 * sinA
srcx = int(x2 + srcw * 0.5 + 0.5)
srcy = int(y2 + srch * 0.5 + 0.5)
if srcx >= 0 and srcx < srcw and srcy >= 0 and srcy < srch then
dstbmp(dsty, dstx) = srcbmp(srcy, srcx)
else
dstbmp(dsty, dstx) = backColor
end if
next
next
Rotate = dstbmp
end function
'在位图中绘制直线
'bitmap-位图;x0,y0,x1,y1-起点和终点坐标
private sub DrawLine(bitmap(), x0, y0, x1, y1)
dim foreColor
dim k, b, x, y, dx, dy
'获取用于线条的颜色代码
foreColor = GetColors()(16)
dx = x1 - x0
dy = y1 - y0
if Abs(dx) > Abs(dy) then
k = (dy / dx)
b = y0 - (k * x0)
if dx < 0 then
dx = -1
else
dx = 1
end if
for x = x0 to x1 step dx
y = Round(k * x + b)
bitmap(y, x) = foreColor
next
elseif dy <> 0 then
k = (dx / dy)
b = x0 - (m * y0)
if dy < 0 then
dy = -1
else
dy = 1
end if
for y = y0 to y1 step dy
x = Round(k * y0 + b)
bitmap(y, x) = foreColor
next
end if
end sub
'向位图中添加随机线条
'bitmap-位图
private sub AddNoise(bitmap())
dim i, x0, y0, x1, y1
dim dx, dy, distance
i = 1
do while i < mvarNoiseLine
'随机获取点
x0 = RandomInterval(0, imageWidth - 1)
y0 = RandomInterval(0, imageHeight - 1)
x1 = RandomInterval(0, imageWidth - 1)
y1 = RandomInterval(0, imageHeight - 1)
dx = Abs(x1 - x0)
dy = Abs(y1 - y0)
distance = Round(sqr(dx * dx + dy * dy))
'如果线条长度不小于 6,则绘制线条
if distance >= 6 then
DrawLine bitmap, x0, y0, x1 , y1
i = i + 1
end if
loop
end sub
'创建验证码
sub Create()
dim captchaCode
dim bmpWidth, bmpHeight
dim bitmap()
dim i, x, y, charbmp
dim angle, image
dim bmpEndLine
dim bmpPalette
dim bmpFH, bmpIH
'获取并存储验证码字符
captchaCode = GetCaptchaCode()
Session(mvarSessionName) = captchaCode
'对合并的位图进行计算
bmpWidth = charWidth * mvarCodeLength
bmpHeight = charHeight
redim bitmap(bmpHeight - 1, bmpWidth - 1)
'拼合位图
for i = 0 to mvarCodeLength - 1
charbmp = GetFormatedCharbmp(mid(captchaCode, i + 1, 1))
for x = 0 to charWidth - 1
for y = 0 to charHeight - 1
bmp(y, i * charWidth + x) = charbmp(y, x)
next
next
next
'旋转位图并获得最终图像
if mvarRandomRotate then
'随机旋转角度
angle = RandomInterval(-45, 45)
else
angle = 0
end if
image = Rotate(bitmap, imageWidth, imageHeight, angle)
AddNoise(image)
'没行的字节如果不是 4 的倍数,则需补零
if (imageWidth mod 4) <> 0 then
bmpEndLine = String((4 - (imageWidth mod 4)) * 2, "0")
else
bmpEndLine = ""
end if
'获取位图调色板
bmpPalette = GetPalette()
'位图文件头:
'0-文件类型(2字节,ox424D)
'1-位图文件的大小(4字节)
'2-保留(2字节)为 0
'3-保留(2字节)为 0
'4-从头文件到图像数据的字节的偏移量(4字节)
bmpFH = Array("424D","00000000","0000","0000","00000000")
'位图信息头:
'0-位图信息头所需要的字节数(4字节)固定值为 40 个字节
'1-图像的宽度(4字节)以像素为单位
'2-图像的高度(4字节)以像素为单位
'3-为目标设备说明颜色平面数(2字节),总是为 1
'4-一个像素所占用的二进制位数(2字节),为 1,4,8,16 或 32
'5-图像的压缩类型(4字节),0 为不压缩
'6-说明图像的大小(4字节),不压缩时可设置为 0
'7-水平分辨率(4字节),0 表示缺省
'8-垂直分辨率(4字节),0 表示缺省
'9-位图实际所使用的颜色表中的颜色索引数,为 0 说明使用所有调色板项
'10-对图像显式有重要影响的颜色索引的数目,为 0 说明都重要
bmpIH = Array("28000000","00000000","00000000","0100","0800","00000000","00000000","00000000","00000000","00000000","00000000")
bmpFH(1) = GetFormatedHexString(Hex((Len(Join(bmpFH,"")) / 2) + (Len(Join(bmpIH,"")) / 2) + (Len(bmpPalette) / 2) + (imageHeight * imageWidth) + (imageHeight * (Len(bmpEndLine) / 2))), 4, true)
bmpFH(4) = GetFormatedHexString(Hex((Len(Join(bmpFH,"")) / 2) + (Len(Join(bmpIH,"")) / 2) + (Len(bmpPalette) / 2)), 4, true)
bmpIH(1) = GetFormatedHexString(Hex(imageWidth),4, true)
bmpIH(2) = GetFormatedHexString(Hex(imageHeight),4, true)
bmpIH(9) = GetFormatedHexString(Hex(Len(bmpPalette) / 8), 4, true)
bmpIH(10) = bmpIH(9)
Response.Clear
Response.Buffer = True
Response.ContentType = "image/bmp"
Response.AddHeader "Content-Disposition", "inline; filename=captcha.bmp"
Response.AddHeader "pragma", "no-cache"
Response.CacheControl = "no-cache"
Response.Expires = -1
'写入位图头文件
WriteHex(Join(bmpFH,""))
WriteHex(Join(bmpIH,""))
WriteHex(bmpPalette)
'写入位图数据
for y = 0 to imageHeight - 1
for x = 0 to imageWidth - 1
WriteHex(image(y, x))
next
WriteHex(bmpEndLine)
next
Response.Flush
end sub
end class
%>