CaptchaAction.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. namespace common\helpers;
  3. use Yii;
  4. use yii\helpers\Url;
  5. use yii\web\Response;
  6. /**
  7. * 验证码扩展类
  8. * @author Leo
  9. * @copyright Copyright (c) 2016 (http://www.LeoCode.net)
  10. */
  11. class CaptchaAction extends \yii\captcha\CaptchaAction
  12. {
  13. const CAPTCHA_CODE = 'ak:captcha_code_%s';
  14. /**
  15. * GD库方式渲染
  16. * @param string $code
  17. * @return string
  18. */
  19. protected function renderImageByGD($code)
  20. {
  21. $this->fontFile = Yii::getAlias('@common/helpers/font/DroidSansMono.ttf');
  22. $image = imagecreatetruecolor($this->width, $this->height);
  23. $backColor = imagecolorallocate(
  24. $image,
  25. (int)($this->backColor % 0x1000000 / 0x10000),
  26. (int)($this->backColor % 0x10000 / 0x100),
  27. $this->backColor % 0x100
  28. );
  29. imagefilledrectangle($image, 0, 0, $this->width - 1, $this->height - 1, $backColor);
  30. imagecolordeallocate($image, $backColor);
  31. $this->transparent = true;
  32. if ($this->transparent) {
  33. imagecolortransparent($image, $backColor);
  34. }
  35. $foreColor = imagecolorallocate(
  36. $image,
  37. (int)($this->foreColor % 0x1000000 / 0x10000),
  38. (int)($this->foreColor % 0x10000 / 0x100),
  39. $this->foreColor % 0x100
  40. );
  41. $length = strlen($code);
  42. $box = imagettfbbox(30, 0, $this->fontFile, $code);
  43. $w = $box[4] - $box[0] + $this->offset * ($length - 1);
  44. $h = $box[1] - $box[5];
  45. $scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);
  46. $x = 10;
  47. $y = round($this->height * 27 / 40);
  48. // 添加彩色乱字符
  49. $chartDictionary = 'abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNOPQRSTUVWXYZ2345679';
  50. for ($i = 0; $i < 50; $i++) {
  51. $chartPosition = rand(0, strlen($chartDictionary));
  52. $chartContent = substr($chartDictionary, $chartPosition, 1);
  53. $chartColor = imagecolorallocate($image, mt_rand(150, 255), mt_rand(150, 255), mt_rand(150, 255));
  54. $chartAngle = rand(-10, 10);
  55. imagettftext($image, 10, $chartAngle, rand(0, ($this->width - $this->padding * 2) - 10), rand(0, ($this->height - $this->padding * 2) - 10), $chartColor, $this->fontFile, $chartContent);
  56. }
  57. // 设置文字
  58. for ($i = 0; $i < $length; ++$i) {
  59. $fontSize = (int)(rand(26, 32) * $scale * 0.8);
  60. //$angle = rand(-10, 10);
  61. $angle = rand(-100, 10);
  62. $letter = $code[$i];
  63. $box = imagettftext($image, $fontSize, $angle, $x, $y, $foreColor, $this->fontFile, $letter);
  64. $x = $box[2] + $this->offset;
  65. }
  66. // 验证码添加线条
  67. for ($i = 0; $i < 10; $i++) {
  68. $lineColor = imagecolorallocate($image, rand(0, 255), rand(0, 255), rand(0, 255));
  69. imageline($image, rand(0, 2), rand(0, ($this->width - $this->height * 2) - 2), rand(0, ($this->width - $this->padding * 2) - 2), rand(0, ($this->width - $this->height * 2) - 2), $lineColor);
  70. }
  71. // 验证码添加噪点
  72. for ($i = 0; $i < 300; $i++) {
  73. //设置点的颜色
  74. $pointColor = imagecolorallocate($image, rand(0, 255), rand(0, 255), rand(0, 255));
  75. imagesetpixel($image, rand(0, ($this->width - $this->padding * 2)), rand(0, ($this->width - $this->height * 2) - 10), $pointColor);
  76. }
  77. imagecolordeallocate($image, $foreColor);
  78. ob_start();
  79. imagepng($image);
  80. imagedestroy($image);
  81. return ob_get_clean();
  82. }
  83. /**
  84. * Runs the action.
  85. */
  86. public function run()
  87. {
  88. if (Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) !== null) {
  89. // AJAX request for regenerating code
  90. $code = $this->getVerifyCode(true);
  91. Yii::$app->response->format = Response::FORMAT_JSON;
  92. return [
  93. 'hash1' => $this->generateValidationHash($code),
  94. 'hash2' => $this->generateValidationHash(strtolower($code)),
  95. // we add a random 'v' parameter so that FireFox can refresh the image
  96. // when src attribute of image tag is changed
  97. 'url' => Url::to([$this->id, 'v' => uniqid()]),
  98. ];
  99. } else {
  100. $this->setHttpHeaders();
  101. Yii::$app->response->format = Response::FORMAT_RAW;
  102. return $this->renderImage($this->getVerifyCode(true));
  103. }
  104. }
  105. /**
  106. * @param bool $regenerate
  107. * @return int|mixed|string|null
  108. */
  109. public function getVerifyCode($regenerate = false)
  110. {
  111. if ($this->fixedVerifyCode !== null) {
  112. return $this->fixedVerifyCode;
  113. }
  114. // $name = $this->getSessionKey();
  115. $pageId = \Yii::$app->request->get('page_id');
  116. if( !$pageId ) {
  117. throw new \Exception('no page id');
  118. }
  119. $cacheRedis = Yii::$app->cache;
  120. $key = sprintf(self::CAPTCHA_CODE, $pageId);
  121. if( !$regenerate ) {
  122. $code = $cacheRedis->get($key);
  123. if ( $code ) return $code;
  124. }
  125. $code = $this->generateVerifyCode();
  126. $cacheRedis->set($key, $code, 300);
  127. return $code;
  128. }
  129. public function validate($input, $caseSensitive)
  130. {
  131. $code = $this->getVerifyCode();
  132. $valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;
  133. if ( $valid ) {
  134. $this->getVerifyCode(true);
  135. }
  136. return $valid;
  137. }
  138. }