CakePHP Components -- CAPTCHA
这是我以前写的一个CAPTCHA(FOR CakePHP)组件, 也在也一直在用。
如果大家喜欢并使用了这个组件,请给我发封邮件: aeon[at]apsou.com
组件代码:
1 class CaptchaComponent extends Object {
2 private $controller;
3 private $fonts;
4 private $length = 6;
5
6 public $caseSensitive = false;
7 public $noise = 0;
8 public $blur = 0;
9 /**
10 * numeric | alpha | alphanumeric
11 **/
12 public $type = 'numeric';
13
14 public $components = array('RequestHandler');
15
16 public function startup(&$controller) {
17 if (!class_exists('Folder')) {
18 App::import('Core', 'Folder');
19 }
20 $this->controller = $controller;
21 $fontpath = WWW_ROOT . 'files' . DS . 'fonts';
22 $folder = new Folder($fontpath);
23 if (false === $folder) {
24 trigger_error('字体目录 files/fonts 不存在.');
25 exit;
26 }
27 $fonts = $folder->find('.+\.ttf');
28 if (empty($fonts) || !is_array($fonts)) {
29 trigger_error('字体目录 files/fonts 中没有正确的字体文件(*.ttf).');
30 exit;
31 }
32 foreach($fonts as &$font) {
33 $font = $fontpath . DS . $font;
34 }
35 $this->fonts = $fonts;
36 unset($fontpath, $fonts);
37 }
38
39 /**
40 * @param array $params
41 **/
42 public function create($model) {
43 if (!is_string($model)) return false;
44 $words = $this->__wordsGen();
45 /**
46 * 写入
47 **/
48 $this->controller->Session->write('CAPTCHA_WORDS', $words);
49 $this->controller->Session->write('CAPTCHA_MODEL', $model);
50 $this->noise = abs($this->noise);
51 $this->blur = abs($this->blur);
52 $this->__makeCaptcha($words);
53 $this->controller->autoRender = false;
54 }
55
56 public function isValid($field = 'captcha') {
57 if (true === $this->RequestHandler->isPost()) {
58 $words = $this->controller->Session->check('CAPTCHA_WORDS') ? $this->controller->Session->read('CAPTCHA_WORDS') : false;
59 $model = $this->controller->Session->check('CAPTCHA_MODEL') ? $this->controller->Session->read('CAPTCHA_MODEL') : false;
60 if (false === $words || false === $model) {
61 return false;
62 }
63 if (isset($this->controller->data[$model][$field]) &&
64 !empty($this->controller->data[$model][$field])) {
65 $input = $this->controller->data[$model][$field];
66 if (true !== $this->caseSensitive) {
67 $input = strtolower($input);
68 $words = strtolower($words);
69 }
70 $result = strcmp($input, $words) === 0;
71 if (true === $result) {
72 $this->controller->Session->del('CAPTCHA_WORDS');
73 $this->controller->Session->del('CAPTCHA_MODEL');
74 unset($this->controller->data[$model][$field]);
75 return true;
76 }
77 }
78 return false;
79 }
80 return false;
81 }
82
83 private function __getRandFonts () {
84 return $this->fonts[mt_rand(0, count($this->fonts) - 1)];
85 }
86
87 /**
88 * 生成随机字符串
89 **/
90 private function __wordsGen() {
91 $results = null;
92
93 switch($this->type) {
94 case 'numeric' : $charPool = range(0, 9); break;
95 case 'alpha' : $charPool = range('A', 'Z'); break;
96 default : $charPool = array_merge(range('A', 'Z'), range(1, 9)); break;
97 }
98
99 if ($this->type !== 'numeric') {
100 if ($this->caseSensitive) {
101 $lowercase = range('a', 'z');
102 $charPool = array_merge($charPool, $lowercase);
103 }
104 }
105
106 $poolLength = count($charPool) - 1;
107
108 for ($i = 0; $i < $this->length; $i++) {
109 $results .= $charPool[mt_rand(0, $poolLength)];
110 }
111
112 return $results;
113 }
114
115 /**
116 * 将文字添加到画板
117 **/
118 private function __signs(&$image, $font, $cells = 3) {
119 $w = imagesx($image);
120 $h = imagesy($image);
121
122 for ($i = 0; $i < $cells; $i++) {
123 $centerX = mt_rand(1, $w);
124 $centerY = mt_rand(1, $h);
125 $amount = mt_rand(1, 15);
126 $stringcolor = imagecolorallocate($image, 200, 200, 200);
127 for ($n = 0; $n < $amount; $n++) {
128 $signs = range('A', 'Z');
129 $sign = $signs[mt_rand(0, count($signs) - 1)];
130 imagettftext($image, 25,
131 mt_rand(-15, 15),
132 $centerX + mt_rand(-50, 50),
133 $centerY + mt_rand(-50, 50),
134 $stringcolor, $font, $sign);
135 }
136 }
137 }
138
139 /**
140 * 添加嗓点
141 **/
142 private function __noise(&$image, $runs = 30) {
143 $w = imagesx($image);
144 $h = imagesy($image);
145 for ($n = 0; $n < $runs; $n++) {
146 for ($i = 1; $i <= $h; $i++) {
147 $randcolor = imagecolorallocate($image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
148 imagesetpixel($image, mt_rand(1, $w), mt_rand(1, $h), $randcolor);
149 }
150 }
151 }
152
153 /**
154 * 模糊
155 **/
156 private function __blur (&$image, $radius = 3) {
157 $radius = round(max(0, min($radius, 50)) * 2);
158 $w = imagesx($image);
159 $h = imagesy($image);
160 $imgBlur = imagecreate($w, $h);
161 for ($i = 0; $i < $radius; $i++) {
162 imagecopy ($imgBlur, $image, 0, 0, 1, 1, $w - 1, $h - 1);
163 imagecopymerge($imgBlur, $image, 1, 1, 0, 0, $w, $h, 50.0000);
164 imagecopymerge($imgBlur, $image, 0, 1, 1, 0, $w - 1, $h, 33.3333);
165 imagecopymerge($imgBlur, $image, 1, 0, 0, 1, $w, $h - 1, 25.0000);
166 imagecopymerge($imgBlur, $image, 0, 0, 1, 0, $w - 1, $h, 33.3333);
167 imagecopymerge($imgBlur, $image, 1, 0, 0, 0, $w, $h, 25.0000);
168 imagecopymerge($imgBlur, $image, 0, 0, 0, 1, $w, $h - 1, 20.0000);
169 imagecopymerge($imgBlur, $image, 0, 1, 0, 0, $w, $h, 16.6667);
170 imagecopymerge($imgBlur, $image, 0, 0, 0, 0, $w, $h, 50.0000);
171 imagecopy ($image , $imgBlur, 0, 0, 0, 0, $w, $h);
172 }
173 imagedestroy($imgBlur);
174 }
175
176 private function __makeCaptcha($words) {
177 $imagelength = $this->length * 25 + 16;
178 $imageheight = 55;
179 $image = imagecreate($imagelength, $imageheight);
180 $bgcolor = imagecolorallocate($image, 255, 255, 255);
181
182 $this->__signs($image, $this->__getRandFonts());
183
184 for ($i = 0; $i < $this->length; $i++) {
185 $stringcolor = imagecolorallocate($image, rand(0, 170), rand(0, 170), rand(0, 170));
186 imagettftext($image, 25, mt_rand(-15, 15), $i * 25 + 10, mt_rand(30, 50), $stringcolor, $this->__getRandFonts(), $words[$i]);
187 }
188
189 if ($this->noise > 0) {
190 $this->__noise($image, $this->noise);
191 }
192 if ($this->blur > 0) {
193 $this->__blur($image, $this->blur);
194 }
195
196 header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
197 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
198 header('Cache-Control: no-cache, must-revalidate');
199 header('Pragma: no-cache');
200 header('Content-type: image/png');
201 imagepng($image);
202 imagedestroy($image);
203 }
204 }
205 ?>
2 private $controller;
3 private $fonts;
4 private $length = 6;
5
6 public $caseSensitive = false;
7 public $noise = 0;
8 public $blur = 0;
9 /**
10 * numeric | alpha | alphanumeric
11 **/
12 public $type = 'numeric';
13
14 public $components = array('RequestHandler');
15
16 public function startup(&$controller) {
17 if (!class_exists('Folder')) {
18 App::import('Core', 'Folder');
19 }
20 $this->controller = $controller;
21 $fontpath = WWW_ROOT . 'files' . DS . 'fonts';
22 $folder = new Folder($fontpath);
23 if (false === $folder) {
24 trigger_error('字体目录 files/fonts 不存在.');
25 exit;
26 }
27 $fonts = $folder->find('.+\.ttf');
28 if (empty($fonts) || !is_array($fonts)) {
29 trigger_error('字体目录 files/fonts 中没有正确的字体文件(*.ttf).');
30 exit;
31 }
32 foreach($fonts as &$font) {
33 $font = $fontpath . DS . $font;
34 }
35 $this->fonts = $fonts;
36 unset($fontpath, $fonts);
37 }
38
39 /**
40 * @param array $params
41 **/
42 public function create($model) {
43 if (!is_string($model)) return false;
44 $words = $this->__wordsGen();
45 /**
46 * 写入
47 **/
48 $this->controller->Session->write('CAPTCHA_WORDS', $words);
49 $this->controller->Session->write('CAPTCHA_MODEL', $model);
50 $this->noise = abs($this->noise);
51 $this->blur = abs($this->blur);
52 $this->__makeCaptcha($words);
53 $this->controller->autoRender = false;
54 }
55
56 public function isValid($field = 'captcha') {
57 if (true === $this->RequestHandler->isPost()) {
58 $words = $this->controller->Session->check('CAPTCHA_WORDS') ? $this->controller->Session->read('CAPTCHA_WORDS') : false;
59 $model = $this->controller->Session->check('CAPTCHA_MODEL') ? $this->controller->Session->read('CAPTCHA_MODEL') : false;
60 if (false === $words || false === $model) {
61 return false;
62 }
63 if (isset($this->controller->data[$model][$field]) &&
64 !empty($this->controller->data[$model][$field])) {
65 $input = $this->controller->data[$model][$field];
66 if (true !== $this->caseSensitive) {
67 $input = strtolower($input);
68 $words = strtolower($words);
69 }
70 $result = strcmp($input, $words) === 0;
71 if (true === $result) {
72 $this->controller->Session->del('CAPTCHA_WORDS');
73 $this->controller->Session->del('CAPTCHA_MODEL');
74 unset($this->controller->data[$model][$field]);
75 return true;
76 }
77 }
78 return false;
79 }
80 return false;
81 }
82
83 private function __getRandFonts () {
84 return $this->fonts[mt_rand(0, count($this->fonts) - 1)];
85 }
86
87 /**
88 * 生成随机字符串
89 **/
90 private function __wordsGen() {
91 $results = null;
92
93 switch($this->type) {
94 case 'numeric' : $charPool = range(0, 9); break;
95 case 'alpha' : $charPool = range('A', 'Z'); break;
96 default : $charPool = array_merge(range('A', 'Z'), range(1, 9)); break;
97 }
98
99 if ($this->type !== 'numeric') {
100 if ($this->caseSensitive) {
101 $lowercase = range('a', 'z');
102 $charPool = array_merge($charPool, $lowercase);
103 }
104 }
105
106 $poolLength = count($charPool) - 1;
107
108 for ($i = 0; $i < $this->length; $i++) {
109 $results .= $charPool[mt_rand(0, $poolLength)];
110 }
111
112 return $results;
113 }
114
115 /**
116 * 将文字添加到画板
117 **/
118 private function __signs(&$image, $font, $cells = 3) {
119 $w = imagesx($image);
120 $h = imagesy($image);
121
122 for ($i = 0; $i < $cells; $i++) {
123 $centerX = mt_rand(1, $w);
124 $centerY = mt_rand(1, $h);
125 $amount = mt_rand(1, 15);
126 $stringcolor = imagecolorallocate($image, 200, 200, 200);
127 for ($n = 0; $n < $amount; $n++) {
128 $signs = range('A', 'Z');
129 $sign = $signs[mt_rand(0, count($signs) - 1)];
130 imagettftext($image, 25,
131 mt_rand(-15, 15),
132 $centerX + mt_rand(-50, 50),
133 $centerY + mt_rand(-50, 50),
134 $stringcolor, $font, $sign);
135 }
136 }
137 }
138
139 /**
140 * 添加嗓点
141 **/
142 private function __noise(&$image, $runs = 30) {
143 $w = imagesx($image);
144 $h = imagesy($image);
145 for ($n = 0; $n < $runs; $n++) {
146 for ($i = 1; $i <= $h; $i++) {
147 $randcolor = imagecolorallocate($image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
148 imagesetpixel($image, mt_rand(1, $w), mt_rand(1, $h), $randcolor);
149 }
150 }
151 }
152
153 /**
154 * 模糊
155 **/
156 private function __blur (&$image, $radius = 3) {
157 $radius = round(max(0, min($radius, 50)) * 2);
158 $w = imagesx($image);
159 $h = imagesy($image);
160 $imgBlur = imagecreate($w, $h);
161 for ($i = 0; $i < $radius; $i++) {
162 imagecopy ($imgBlur, $image, 0, 0, 1, 1, $w - 1, $h - 1);
163 imagecopymerge($imgBlur, $image, 1, 1, 0, 0, $w, $h, 50.0000);
164 imagecopymerge($imgBlur, $image, 0, 1, 1, 0, $w - 1, $h, 33.3333);
165 imagecopymerge($imgBlur, $image, 1, 0, 0, 1, $w, $h - 1, 25.0000);
166 imagecopymerge($imgBlur, $image, 0, 0, 1, 0, $w - 1, $h, 33.3333);
167 imagecopymerge($imgBlur, $image, 1, 0, 0, 0, $w, $h, 25.0000);
168 imagecopymerge($imgBlur, $image, 0, 0, 0, 1, $w, $h - 1, 20.0000);
169 imagecopymerge($imgBlur, $image, 0, 1, 0, 0, $w, $h, 16.6667);
170 imagecopymerge($imgBlur, $image, 0, 0, 0, 0, $w, $h, 50.0000);
171 imagecopy ($image , $imgBlur, 0, 0, 0, 0, $w, $h);
172 }
173 imagedestroy($imgBlur);
174 }
175
176 private function __makeCaptcha($words) {
177 $imagelength = $this->length * 25 + 16;
178 $imageheight = 55;
179 $image = imagecreate($imagelength, $imageheight);
180 $bgcolor = imagecolorallocate($image, 255, 255, 255);
181
182 $this->__signs($image, $this->__getRandFonts());
183
184 for ($i = 0; $i < $this->length; $i++) {
185 $stringcolor = imagecolorallocate($image, rand(0, 170), rand(0, 170), rand(0, 170));
186 imagettftext($image, 25, mt_rand(-15, 15), $i * 25 + 10, mt_rand(30, 50), $stringcolor, $this->__getRandFonts(), $words[$i]);
187 }
188
189 if ($this->noise > 0) {
190 $this->__noise($image, $this->noise);
191 }
192 if ($this->blur > 0) {
193 $this->__blur($image, $this->blur);
194 }
195
196 header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
197 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
198 header('Cache-Control: no-cache, must-revalidate');
199 header('Pragma: no-cache');
200 header('Content-type: image/png');
201 imagepng($image);
202 imagedestroy($image);
203 }
204 }
205 ?>
应用:
在控制器中, 比如 UsersController
1class UsersController extends AppController {
2
3 .
4
5 public $components = array('Captcha');
6
7 public function captcha($modelName = 'User') {
8 $modelName = Inflector::camelize($modelName);
9 $this->autoRender = false;
10 $this->Captcha->noise = 5; //嗓点
11 $this->Captcha->blur = 0; //模糊效果级别, 0为不使用模糊
12 $this->Captcha->type = 'alpha'; //numeric | alpha | alphpnumeric
13 $this->Captcha->caseSensitive = false; //是否区分大小写, 只在非纯数字情况下有效
14 $this->Captcha->create($modelName); //创建
15 }
16
17
18
19 public function login() {
20
21 if (!empty($this->data)) {
22 if ($this->Captcha->isValid()) {
23 if ($this->User->verify($this->data)) {
24 .
25 }
26 } else {
27 $this->Session->setFlash('验证码错误, 请重新输入.');
28 }
29 }
30
31 } .
32
33}
2
3 .
4
5 public $components = array('Captcha');
6
7 public function captcha($modelName = 'User') {
8 $modelName = Inflector::camelize($modelName);
9 $this->autoRender = false;
10 $this->Captcha->noise = 5; //嗓点
11 $this->Captcha->blur = 0; //模糊效果级别, 0为不使用模糊
12 $this->Captcha->type = 'alpha'; //numeric | alpha | alphpnumeric
13 $this->Captcha->caseSensitive = false; //是否区分大小写, 只在非纯数字情况下有效
14 $this->Captcha->create($modelName); //创建
15 }
16
17
18
19 public function login() {
20
21 if (!empty($this->data)) {
22 if ($this->Captcha->isValid()) {
23 if ($this->User->verify($this->data)) {
24 .
25 }
26 } else {
27 $this->Session->setFlash('验证码错误, 请重新输入.');
28 }
29 }
30
31 } .
32
33}
在 view :(users/login.ctp)
1 <?php echo $html->image('/users/captcha/user', array('id' => 'captcha_image')); ?><span class="explain">不区分大小写. 如果看不清楚, 请<a href="javascript:reloadCAPTCHA();">换一张</a>再试.</a>
2<script type="text/javascript">
3function reloadCAPTCHA() {
4 var img = $('captcha_image');
5 if (img && img.src != undefined) {
6 img.src = '/users/captcha/user/' + Math.random(1000, 9999);
7 }
8}
9</script>
2<script type="text/javascript">
3function reloadCAPTCHA() {
4 var img = $('captcha_image');
5 if (img && img.src != undefined) {
6 img.src = '/users/captcha/user/' + Math.random(1000, 9999);
7 }
8}
9</script>