CaptchaAction.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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. $letter = $code[$i];
  62. $box = imagettftext($image, $fontSize, $angle, $x, $y, $foreColor, $this->fontFile, $letter);
  63. $x = $box[2] + $this->offset;
  64. }
  65. // 验证码添加线条
  66. for ($i = 0; $i < 10; $i++) {
  67. $lineColor = imagecolorallocate($image, rand(0, 255), rand(0, 255), rand(0, 255));
  68. 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);
  69. }
  70. // 验证码添加噪点
  71. for ($i = 0; $i < 300; $i++) {
  72. //设置点的颜色
  73. $pointColor = imagecolorallocate($image, rand(0, 255), rand(0, 255), rand(0, 255));
  74. imagesetpixel($image, rand(0, ($this->width - $this->padding * 2)), rand(0, ($this->width - $this->height * 2) - 10), $pointColor);
  75. }
  76. imagecolordeallocate($image, $foreColor);
  77. ob_start();
  78. imagepng($image);
  79. imagedestroy($image);
  80. return ob_get_clean();
  81. }
  82. /**
  83. * Runs the action.
  84. */
  85. public function run()
  86. {
  87. if (Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) !== null) {
  88. // AJAX request for regenerating code
  89. $code = $this->getVerifyCode(true);
  90. Yii::$app->response->format = Response::FORMAT_JSON;
  91. return [
  92. 'hash1' => $this->generateValidationHash($code),
  93. 'hash2' => $this->generateValidationHash(strtolower($code)),
  94. // we add a random 'v' parameter so that FireFox can refresh the image
  95. // when src attribute of image tag is changed
  96. 'url' => Url::to([$this->id, 'v' => uniqid()]),
  97. ];
  98. } else {
  99. $this->setHttpHeaders();
  100. Yii::$app->response->format = Response::FORMAT_RAW;
  101. return $this->renderImage($this->getVerifyCode(true));
  102. }
  103. }
  104. /**
  105. * @param bool $regenerate
  106. * @return int|mixed|string|null
  107. */
  108. public function getVerifyCode($regenerate = false)
  109. {
  110. if ($this->fixedVerifyCode !== null) {
  111. return $this->fixedVerifyCode;
  112. }
  113. // $name = $this->getSessionKey();
  114. $pageId = \Yii::$app->request->get('page_id');
  115. if( !$pageId ) {
  116. throw new \Exception('no page id');
  117. }
  118. $cacheRedis = Yii::$app->cache;
  119. $key = sprintf(self::CAPTCHA_CODE, $pageId);
  120. if( !$regenerate ) {
  121. $code = $cacheRedis->get($key);
  122. if ( $code ) return $code;
  123. }
  124. $code = $this->generateVerifyCode();
  125. $cacheRedis->set($key, $code, 300);
  126. return $code;
  127. }
  128. public function validate($input, $caseSensitive)
  129. {
  130. $code = $this->getVerifyCode();
  131. $valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;
  132. if ( $valid ) {
  133. $this->getVerifyCode(true);
  134. }
  135. return $valid;
  136. }
  137. }