在前一篇文章中初步的实现了验证码的随机生成,觉得很容易被破解,因此加了一些干扰;
鹤冲天建议使用表达式作为验证码,即使破解程序识别了验证码,也要计算结果才能通过验证,在一定程度上增加了破解难度。
因此我对代码进行了重新整理,通过控制运行时配置选项来达到不同效果。
现在主要做到了:
(1)字符内容随机(普通字符串或表达式)
(2)每个字符的字体随机
(3)每个字符大小随机
(4)每个字符倾斜角度随机
(5)干扰线条随机
除了(4)其他都可以通过配置来控制。
下面是代码,有详细的注释,就不多说了:
1
require
(
"
gd
"
)
2 require ( " lfs " )
3
4 -- ---------------------------------------------------------------------------------------------------------------------------------
5 -- 运行配置项
6 -- ---------------------------------------------------------------------------------------------------------------------------------
7 -- 字体:-1-使用gd.FONT_GIANT字体;1-使用随机字体;其他-使用“fonts”中第一个字体
8 -- 字体预先在变量“fonts”中定义;如果fonts没有值,将搜索系统中的所有字体,字体路径在“FONT_PATH”中预定义
9 FONT = 1
10
11 -- 每个字符字体随机:1-是,其他-否
12 -- 仅当“FONT”值为“1”时,本变量起作用
13 FONT_RANDOM_CHAR = 0
14
15 -- 每个字符字体大小是否随机:1-是,其他-否
16 -- 仅当FONT^=-1时起作用
17 FONT_SIZE_RANDOM = 1
18
19 -- 是否增加线条干扰:1-是;其他-否
20 XLINE_FALG = 1
21
22 -- 干扰线条的最多条数
23 -- 仅当“XLINE_FALG”的值为“是”是,本变量起作用
24 XLINE_LIMIT = 6
25
26 -- 验证码类型:TEXT-字符串;EXPRESSION-表达式
27 MARK_TYPE = " TEXT "
28 -- MARK_TYPE="EXPRESSION"
29
30 -- 字符个数:仅当“MARK_TYPE”=“TEXT”时,本变量起作用
31 TEXT_NUM = 6
32
33 -- 表达式项数限制(最多不超过EXPRESSION_ITEMS项):仅当“MARK_TYPE”=“EXPRESSION”时,本变量起作用
34 EXPRESSION_ITEMS = 3
35
36 -- 生成验证码个数
37 MARK_NUM = 100000
38
39
40 -- ---------------------------------------------------------------------------------------------------------------------------------
41 -- 预定义变量
42 -- ---------------------------------------------------------------------------------------------------------------------------------
43 -- 词典
44 dict = { ' a ' , ' b ' , ' c ' , ' d ' , ' e ' , ' f ' , ' g ' , ' h ' , ' i ' , ' j ' , ' k ' , ' l ' , ' m ' , ' n ' , ' o ' , ' p ' , ' q ' , ' r ' , ' s ' , ' t ' , ' u ' , ' v ' , ' w ' , ' x ' , ' y ' , ' z ' , ' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' , ' G ' , ' H ' , ' I ' , ' J ' , ' K ' , ' L ' , ' M ' , ' N ' , ' O ' , ' P ' , ' Q ' , ' R ' , ' S ' , ' T ' , ' U ' , ' V ' , ' W ' , ' X ' , ' Y ' , ' Z ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' , ' 8 ' , ' 9 ' , ' 0 ' }
45 numbers = { " 1 " , " 2 " , " 3 " , " 4 " , " 5 " , " 6 " , " 7 " , " 8 " , " 9 " } -- 表达式可使用数字,排除0
46 operators = { " + " , " - " , " * " } -- 表达式可使用运算符,不支持“/”
47
48 -- 随机种子,防止通过获取系统时间得到随机数种子,直接计算出验证码
49 math.randomseed ( os.time ())
50
51 -- 大小
52 IMG_WIDTH = 100
53 IMG_HEIGHT = 40
54
55 -- 颜色
56 im2 = gd.createTrueColor(IMG_WIDTH,IMG_HEIGHT)
57 fg = im2:colorAllocate( 129 , 32 , 28 ) -- 前景色
58 bg = im2:colorAllocate( 216 , 235 , 238 ) -- 背景色
59
60 -- 字体
61 FONT_PATH = " C:/WINDOWS/Fonts/ " -- 系统字体路径
62 fonts = {}
63 -- fonts={"courbd.ttf","courbi.ttf","DejaVuMonoSans.ttf","DejaVuMonoSansBold.ttf","DejaVuMonoSansBoldOblique.ttf","DejaVuMonoSansOblique.ttf","lucon.ttf","monosbi.ttf","nina.ttf","simhei.ttf","simkai.ttf","swissci.ttf","tahomabd.ttf","timesbd.ttf","timesbi.ttf","timesi.ttf","trebuc.ttf","trebucit.ttf"}
64 font_size = { 14 , 15 , 16 , 17 , 18 , 19 , 20 } -- 随机字体大小
65
66 -- 生成的随机码
67 stringmark = ""
68
69 -- ---------------------------------------------------------------------------------------------------------------------------------
70 -- 功能函数
71 -- ---------------------------------------------------------------------------------------------------------------------------------
72 -- 初始化:创建图片、设置背景
73 function init()
74 im2 = gd.createTrueColor(IMG_WIDTH, IMG_HEIGHT)
75 im2:filledRectangle( 0 , 0 ,IMG_WIDTH,IMG_HEIGHT,bg)
76 stringmark = ""
77 end
78
79 -- 查找字体
80 function searchFont()
81 if table.getn(fonts) == 0 then -- 没有指定字体,就搜索系统字体
82 local i = 1
83 for file in lfs.dir(FONT_PATH) do
84 if string.find (file, " .ttf " ) and not string.find (file, " esri " ) then -- 排除特定字体
85 fonts[i] = file
86 i = i + 1
87 end
88 end
89 end
90 end
91
92 -- 生成text字符串
93 function makeText()
94 local num = table.getn(dict)
95 for i = 1 ,TEXT_NUM do
96 stringmark = stringmark..dict[ math.random (num)]
97 end
98 end
99
100 -- 生成表达式字符串
101 function makeExpression()
102 local n = math.random ( 2 , 3 ) -- 表达式项数
103 local strings = {}
104 for i = 1 ,n * 2 - 1 do -- 表达式项数+运算符项数
105 local str = ""
106 if i % 2 == 1 then -- 数字
107 local n2 = math.random ( 1 , 2 )
108 for j = 1 ,n2 do -- 每个数字最多2位
109 str = str..numbers[ math.random ( 9 )]
110 end
111 else -- 运算符
112 str = operators[ math.random ( 3 )]
113 end
114 strings[i] = str
115 end
116 return strings
117 end
118
119 -- 主函数
120 function doIt()
121 searchFont()
122 local numfonts = table.getn(fonts)
123 if numfonts < 1 then
124 print ( " 没有找到字体! " )
125 return
126 end
127
128 for i = 1 ,MARK_NUM do
129 init()
130 local font = fonts[ 0 ];
131 local fontsize = 20 ;
132
133 if MARK_TYPE == " TEXT " then -- 普通字符串验证码
134 makeText()
135 -- print(stringmark)
136 if FONT ==- 1 then
137 im2:string(gd.FONT_GIANT, 18 , 10 ,stringmark, fg)
138 else
139 for nIndex = 1 , string.len (stringmark) do
140 if FONT == 1 then font = fonts[ math.random (numfonts)] end
141 if FONT_SIZE_RANDOM == 1 then fontsize = font_size[ math.random ( 7 )] end
142 im2:stringFT(fg,FONT_PATH..font,fontsize, math.random () / math.pi , 5 + (nIndex - 1 ) * 15 , 25 , string.sub (stringmark,nIndex,nIndex))
143 end
144 end
145 elseif MARK_TYPE == " EXPRESSION " then -- 表达式验证码
146 local strings = makeExpression()
147 local raise = 0
148 local ncharacter = 0
149 for j = 1 ,table.getn(strings) do
150 if j % 2 == 0 then raise = 3 end
151 stringmark = stringmark..strings[j]
152 if FONT == 1 then font = fonts[ math.random (numfonts)] end
153 if FONT_SIZE_RANDOM == 1 then fontsize = font_size[ math.random ( 5 )] end
154 im2:stringFT(fg,FONT_PATH..font,fontsize, math.random () / math.pi , 5 + ncharacter * 12 + raise, 25 , strings[j])
155 ncharacter = ncharacter + string.len (strings[j])
156 end
157 -- print(stringmark)
158 -- value=tonumber(stringmark)
159 -- print(value)
160 end
161
162 -- 随机线条干扰
163 if XLINE_FALG == 1 then
164 local xlineNum = math.random (XLINE_LIMIT)
165 for i = 1 ,xlineNum do
166 im2:line( math.random (IMG_WIDTH), math.random (IMG_HEIGHT), math.random (IMG_WIDTH), math.random (IMG_HEIGHT),fg)
167 end
168 end
169
170 im2:png( " ./output/ " ..font.. " .png " , 100 )
171 end
172 end
173
174 -- start = os.clock ()
175 doIt()
176 -- print ( os.clock () - start)
2 require ( " lfs " )
3
4 -- ---------------------------------------------------------------------------------------------------------------------------------
5 -- 运行配置项
6 -- ---------------------------------------------------------------------------------------------------------------------------------
7 -- 字体:-1-使用gd.FONT_GIANT字体;1-使用随机字体;其他-使用“fonts”中第一个字体
8 -- 字体预先在变量“fonts”中定义;如果fonts没有值,将搜索系统中的所有字体,字体路径在“FONT_PATH”中预定义
9 FONT = 1
10
11 -- 每个字符字体随机:1-是,其他-否
12 -- 仅当“FONT”值为“1”时,本变量起作用
13 FONT_RANDOM_CHAR = 0
14
15 -- 每个字符字体大小是否随机:1-是,其他-否
16 -- 仅当FONT^=-1时起作用
17 FONT_SIZE_RANDOM = 1
18
19 -- 是否增加线条干扰:1-是;其他-否
20 XLINE_FALG = 1
21
22 -- 干扰线条的最多条数
23 -- 仅当“XLINE_FALG”的值为“是”是,本变量起作用
24 XLINE_LIMIT = 6
25
26 -- 验证码类型:TEXT-字符串;EXPRESSION-表达式
27 MARK_TYPE = " TEXT "
28 -- MARK_TYPE="EXPRESSION"
29
30 -- 字符个数:仅当“MARK_TYPE”=“TEXT”时,本变量起作用
31 TEXT_NUM = 6
32
33 -- 表达式项数限制(最多不超过EXPRESSION_ITEMS项):仅当“MARK_TYPE”=“EXPRESSION”时,本变量起作用
34 EXPRESSION_ITEMS = 3
35
36 -- 生成验证码个数
37 MARK_NUM = 100000
38
39
40 -- ---------------------------------------------------------------------------------------------------------------------------------
41 -- 预定义变量
42 -- ---------------------------------------------------------------------------------------------------------------------------------
43 -- 词典
44 dict = { ' a ' , ' b ' , ' c ' , ' d ' , ' e ' , ' f ' , ' g ' , ' h ' , ' i ' , ' j ' , ' k ' , ' l ' , ' m ' , ' n ' , ' o ' , ' p ' , ' q ' , ' r ' , ' s ' , ' t ' , ' u ' , ' v ' , ' w ' , ' x ' , ' y ' , ' z ' , ' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' , ' G ' , ' H ' , ' I ' , ' J ' , ' K ' , ' L ' , ' M ' , ' N ' , ' O ' , ' P ' , ' Q ' , ' R ' , ' S ' , ' T ' , ' U ' , ' V ' , ' W ' , ' X ' , ' Y ' , ' Z ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' , ' 8 ' , ' 9 ' , ' 0 ' }
45 numbers = { " 1 " , " 2 " , " 3 " , " 4 " , " 5 " , " 6 " , " 7 " , " 8 " , " 9 " } -- 表达式可使用数字,排除0
46 operators = { " + " , " - " , " * " } -- 表达式可使用运算符,不支持“/”
47
48 -- 随机种子,防止通过获取系统时间得到随机数种子,直接计算出验证码
49 math.randomseed ( os.time ())
50
51 -- 大小
52 IMG_WIDTH = 100
53 IMG_HEIGHT = 40
54
55 -- 颜色
56 im2 = gd.createTrueColor(IMG_WIDTH,IMG_HEIGHT)
57 fg = im2:colorAllocate( 129 , 32 , 28 ) -- 前景色
58 bg = im2:colorAllocate( 216 , 235 , 238 ) -- 背景色
59
60 -- 字体
61 FONT_PATH = " C:/WINDOWS/Fonts/ " -- 系统字体路径
62 fonts = {}
63 -- fonts={"courbd.ttf","courbi.ttf","DejaVuMonoSans.ttf","DejaVuMonoSansBold.ttf","DejaVuMonoSansBoldOblique.ttf","DejaVuMonoSansOblique.ttf","lucon.ttf","monosbi.ttf","nina.ttf","simhei.ttf","simkai.ttf","swissci.ttf","tahomabd.ttf","timesbd.ttf","timesbi.ttf","timesi.ttf","trebuc.ttf","trebucit.ttf"}
64 font_size = { 14 , 15 , 16 , 17 , 18 , 19 , 20 } -- 随机字体大小
65
66 -- 生成的随机码
67 stringmark = ""
68
69 -- ---------------------------------------------------------------------------------------------------------------------------------
70 -- 功能函数
71 -- ---------------------------------------------------------------------------------------------------------------------------------
72 -- 初始化:创建图片、设置背景
73 function init()
74 im2 = gd.createTrueColor(IMG_WIDTH, IMG_HEIGHT)
75 im2:filledRectangle( 0 , 0 ,IMG_WIDTH,IMG_HEIGHT,bg)
76 stringmark = ""
77 end
78
79 -- 查找字体
80 function searchFont()
81 if table.getn(fonts) == 0 then -- 没有指定字体,就搜索系统字体
82 local i = 1
83 for file in lfs.dir(FONT_PATH) do
84 if string.find (file, " .ttf " ) and not string.find (file, " esri " ) then -- 排除特定字体
85 fonts[i] = file
86 i = i + 1
87 end
88 end
89 end
90 end
91
92 -- 生成text字符串
93 function makeText()
94 local num = table.getn(dict)
95 for i = 1 ,TEXT_NUM do
96 stringmark = stringmark..dict[ math.random (num)]
97 end
98 end
99
100 -- 生成表达式字符串
101 function makeExpression()
102 local n = math.random ( 2 , 3 ) -- 表达式项数
103 local strings = {}
104 for i = 1 ,n * 2 - 1 do -- 表达式项数+运算符项数
105 local str = ""
106 if i % 2 == 1 then -- 数字
107 local n2 = math.random ( 1 , 2 )
108 for j = 1 ,n2 do -- 每个数字最多2位
109 str = str..numbers[ math.random ( 9 )]
110 end
111 else -- 运算符
112 str = operators[ math.random ( 3 )]
113 end
114 strings[i] = str
115 end
116 return strings
117 end
118
119 -- 主函数
120 function doIt()
121 searchFont()
122 local numfonts = table.getn(fonts)
123 if numfonts < 1 then
124 print ( " 没有找到字体! " )
125 return
126 end
127
128 for i = 1 ,MARK_NUM do
129 init()
130 local font = fonts[ 0 ];
131 local fontsize = 20 ;
132
133 if MARK_TYPE == " TEXT " then -- 普通字符串验证码
134 makeText()
135 -- print(stringmark)
136 if FONT ==- 1 then
137 im2:string(gd.FONT_GIANT, 18 , 10 ,stringmark, fg)
138 else
139 for nIndex = 1 , string.len (stringmark) do
140 if FONT == 1 then font = fonts[ math.random (numfonts)] end
141 if FONT_SIZE_RANDOM == 1 then fontsize = font_size[ math.random ( 7 )] end
142 im2:stringFT(fg,FONT_PATH..font,fontsize, math.random () / math.pi , 5 + (nIndex - 1 ) * 15 , 25 , string.sub (stringmark,nIndex,nIndex))
143 end
144 end
145 elseif MARK_TYPE == " EXPRESSION " then -- 表达式验证码
146 local strings = makeExpression()
147 local raise = 0
148 local ncharacter = 0
149 for j = 1 ,table.getn(strings) do
150 if j % 2 == 0 then raise = 3 end
151 stringmark = stringmark..strings[j]
152 if FONT == 1 then font = fonts[ math.random (numfonts)] end
153 if FONT_SIZE_RANDOM == 1 then fontsize = font_size[ math.random ( 5 )] end
154 im2:stringFT(fg,FONT_PATH..font,fontsize, math.random () / math.pi , 5 + ncharacter * 12 + raise, 25 , strings[j])
155 ncharacter = ncharacter + string.len (strings[j])
156 end
157 -- print(stringmark)
158 -- value=tonumber(stringmark)
159 -- print(value)
160 end
161
162 -- 随机线条干扰
163 if XLINE_FALG == 1 then
164 local xlineNum = math.random (XLINE_LIMIT)
165 for i = 1 ,xlineNum do
166 im2:line( math.random (IMG_WIDTH), math.random (IMG_HEIGHT), math.random (IMG_WIDTH), math.random (IMG_HEIGHT),fg)
167 end
168 end
169
170 im2:png( " ./output/ " ..font.. " .png " , 100 )
171 end
172 end
173
174 -- start = os.clock ()
175 doIt()
176 -- print ( os.clock () - start)
效果大致如下: