最早看到 “贴吧神兽” 验证码是在百度贴吧,吧主防止挖坟贴,放出了究极神兽验证码

例如:

地址:http://tieba.baidu.com/p/3320323440

可以用 PHP + JavaScript 实现该种类型的验证码。

使用 jQuery 版本:jQuery 1.9.1

框架使用 ThinkPHP 3.2.3,自定义的验证码类基于 TP 的验证码类

最终效果图:

自定义验证码类路径:/Application/Home/Common/VerivyPostBar.class.php

控制器:/Application/Home/Controller/PostBarController.class.php

视图:/Applicable/Home/View/PostBarVerify/index.html

自定义验证码类 /Application/Home/Common/VerivyPostBar.class.php

<?php

namespace Home\Common;
use Think\Verify; class VerifyPostBar extends Verify { private $_image = NULL; // 验证码图片实例
private $_color = NULL; // 验证码字体颜色 public function entryProcess($id = '') {
// 图片宽(px)
$this->imageW || $this->imageW = $this->length*$this->fontSize*1.5 + $this->length*$this->fontSize/2;
// 图片高(px)
$this->imageH || $this->imageH = $this->fontSize * 2.5; // 建立一幅 $this->imageW x $this->imageH 的透明图像
$this->_image = imagecreatetruecolor($this->imageW, $this->imageH);
imagesavealpha($this->_image, true);
$trans_colour = imagecolorallocatealpha($this->_image, 0, 0, 0, 127);
imagefill($this->_image, 0, 0, $trans_colour); // 验证码字体随机颜色
$this->_color = imagecolorallocate($this->_image, mt_rand(1,150), mt_rand(1,150), mt_rand(1,150));
// 验证码使用随机字体
$ttfPath = $_SERVER['DOCUMENT_ROOT'].'/ThinkPHP/Library/Think/Verify/' . ($this->useZh ? 'zhttfs' : 'ttfs') . '/'; if(empty($this->fontttf)){
$dir = dir($ttfPath);
$ttfs = array();
while (false !== ($file = $dir->read())) {
if($file[0] != '.' && substr($file, -4) == '.ttf') {
$ttfs[] = $file;
}
}
$dir->close();
$this->fontttf = $ttfs[array_rand($ttfs)];
}
$this->fontttf = $ttfPath . $this->fontttf; if($this->useImgBg) {
$this->_background();
} if ($this->useNoise) {
// 绘杂点
// $this->_writeNoise();
}
if ($this->useCurve) {
// 绘干扰线
$this->_writeCurve();
} // 绘验证码
$code = array(); // 验证码
$codeNX = 0; // 验证码第N个字符的左边距 if($this->useZh){ // 中文验证码
for ($i = 0; $i<$this->length; $i++) {
$code[$i] = iconv_substr($this->zhSet, $i, 1, 'utf-8');
imagettftext($this->_image, $this->fontSize, mt_rand(-40, 40), $this->fontSize*($i+1)*1.5, $this->fontSize + mt_rand(10, 20), $this->_color, $this->fontttf, $code[$i]);
} // 备选验证码区域(9个汉字)
$len_pre_row = $this->area_length / $this->rows; // 每行的字数
for($r = 0; $r < $this->rows; $r++) {
$flag = 1;
$start = $r * $len_pre_row;
$end = $r * $len_pre_row + $len_pre_row - 1;
$code_ = array();
for ($i = $start; $i<$end + 1; $i++) {
$code_[$i] = iconv_substr($this->code_area, $i, 1, 'utf-8');
// @param image
// @param size
// @param angle
// @param x
// @param y
// @param color
// @param fontfile
imagettftext($this->_image, $this->fontSize, mt_rand(-20, 20), $this->fontSize*2 * $flag, $this->fontSize + 50 * $r + 120, $this->_color, $this->fontttf, $code_[$i]);
$flag += 2; // 控制验证码备选字符的x坐标
}
}
} // 保存验证码
$key = $this->authcode($this->seKey);
$code = $this->authcode(strtoupper(implode('', $code)));
$secode = array();
$secode['verify_code'] = $code; // 把校验码保存到session
$secode['verify_time'] = NOW_TIME; // 验证码创建时间
session($key.$id, $secode); header('Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header("content-type: image/png"); // 保存图像至硬盘
imagepng($this->_image, 'Public/Home/Images/verifyimage.png');
// 输出图像
// imagepng($this->_image);
readfile('Public/Home/Images/verifyimage.png');
imagedestroy($this->_image);
} /**
* 画杂点
* 往图片上写不同颜色的字母或数字
*/
private function _writeNoise() {
$codeSet = '2345678abcdefhijkmnpqrstuvwxyz';
for($i = 0; $i < 10; $i++){
//杂点颜色
$noiseColor = imagecolorallocate($this->_image, mt_rand(150,225), mt_rand(150,225), mt_rand(150,225));
for($j = 0; $j < 5; $j++) {
// 绘杂点
imagestring($this->_image, 5, mt_rand(-10, $this->imageW), mt_rand(-10, $this->imageH), $codeSet[mt_rand(0, 29)], $noiseColor);
}
}
} /**
* 画一条由两条连在一起构成的随机正弦函数曲线作干扰线(你可以改成更帅的曲线函数)
*
* 高中的数学公式咋都忘了涅,写出来
* 正弦型函数解析式:y=Asin(ωx+φ)+b
* 各常数值对函数图像的影响:
* A:决定峰值(即纵向拉伸压缩的倍数)
* b:表示波形在Y轴的位置关系或纵向移动距离(上加下减)
* φ:决定波形与X轴位置关系或横向移动距离(左加右减)
* ω:决定周期(最小正周期T=2π/∣ω∣)
*
*/
private function _writeCurve() {
$px = $py = 0; // 曲线前部分
$A = mt_rand(1, $this->imageVerifyH/2); // 振幅
$b = mt_rand(-$this->imageVerifyH/4, $this->imageVerifyH/4); // Y轴方向偏移量
$f = mt_rand(-$this->imagimageVerifyHeH/4, $this->imageVerifyH/4); // X轴方向偏移量
$T = mt_rand($this->imageVerifyH, $this->imageW*2); // 周期
$w = (2* M_PI)/$T; $px1 = 0; // 曲线横坐标起始位置
$px2 = mt_rand($this->imageW/2, $this->imageW * 0.8); // 曲线横坐标结束位置 for ($px=$px1; $px<=$px2; $px = $px + 1) {
if ($w!=0) {
$py = $A * sin($w*$px + $f)+ $b + $this->imageVerifyH/2; // y = Asin(ωx+φ) + b
$i = (int) ($this->fontSize/5);
while ($i > 0) {
imagesetpixel($this->_image, $px + $i , $py + $i, $this->_color); // 这里(while)循环画像素点比imagettftext和imagestring用字体大小一次画出(不用这while循环)性能要好很多
$i--;
}
}
}
} /* 加密验证码 */
private function authcode($str){
$key = substr(md5($this->seKey), 5, 8);
$str = substr(md5($str), 8, 10);
return md5($key . $str);
} /**
* 绘制背景图片
* 注:如果验证码输出图片比较大,将占用比较多的系统资源
*/
private function _background() {
$path = dirname(__FILE__).'/Verify/bgs/';
$dir = dir($path); $bgs = array();
while (false !== ($file = $dir->read())) {
if($file[0] != '.' && substr($file, -4) == '.jpg') {
$bgs[] = $path . $file;
}
}
$dir->close(); $gb = $bgs[array_rand($bgs)]; list($width, $height) = @getimagesize($gb);
// Resample
$bgImage = @imagecreatefromjpeg($gb);
@imagecopyresampled($this->_image, $bgImage, 0, 0, 0, 0, $this->imageW, $this->imageH, $width, $height);
@imagedestroy($bgImage);
}
}

  

控制器 /Application/Home/Controller/PostBarController.class.php

<?php

namespace Home\Controller;
use Think\Controller;
use Home\Common\VerifyPostBar; class PostBarVerifyController extends Controller { // 界面
public function index() {
header('Content-type:text/html;charset=utf-8');
$this->display();
} // 验证
public function check_verify($code) { $verify = new VerifyPostBar();
if(!$verify->check($code)) {
return 400;
} else {
return 200;
}
} // 准备验证码字符
public function prepare_code() {
// 验证码的长度
$length = 4;
// 验证码选区长度
$selects = 12; // 相近的汉字为一组,从6组36个汉字中抽出4组12个汉字组成验证码图片组
$zhSet = array(
array(
'已','己','乙','巳','九','走'
),
array(
'田','由','甲','申','白','日'
),
array(
'鱼','渔','俞','喻','瑜','愈'
),
array(
'请','清','情','青','晴','蜻'
),
array(
'宝','玉','穴','必','空','控'
),
array(
'子','仔','籽','孜','吱','资'
)
); $tmp = array();
$count = count($zhSet);
$tmp = $this->rand(0, $count - 1, 4); // 随机生成4个不重复的数(0-5组里面选出4组)作为下标
$chars = array();
foreach($tmp as $key => $val) {
$chars[] = $this->choose($zhSet, $val, 3);// 每组3个数
} // 从每组一维数组中选出一个组成长度为4的验证码
foreach($chars as $key => $val) {
$k = mt_rand(0, count($val) - 1);
$code[] = $val[$k]; // 验证码
unset($chars[$key][$k]);
} // dump($code);
// dump($chars);die; // 把数组合并成一维数组
$characters = array();
foreach($chars as $key => $val) {
foreach($val as $k => $v) {
$characters[] = $v;
}
} // 备选验证码区数组
$code_area_array = array_merge($code, $characters); shuffle($code_area_array);
// 备选验证码区字符串
$code_area = implode('', $code_area_array);
$code = implode('', $code); $codes['code_area'] = $code_area;
$codes['code_area_array'] = $code_area_array;
$codes['code'] = $code;
$codes['characters'] = $characters;
$codes['length'] = $length; $_SESSION['code_area_array'] = $code_area_array; return $codes; } // 显示验证码
public function verify() { $codes = $this->prepare_code(); $conf = array(
'useZh' => true,
'zhSet' => $codes['code'],
'code_area' => $codes['code_area'],
'length' => $codes['length'], // 验证码长度(汉字个数)
'rows' => 3, //备选区域3行
'area_length'=> mb_strlen($codes['code_area'], 'utf-8'), // 备选区域汉字个数
'fontSize' => 20,
'imageW' => 320,
'imageH' => 600,
'imageVerifyH' => 45, // 4字验证码区域高度
);
$verify = new VerifyPostBar($conf);
$verify->entryProcess();
} // 从一组连续的数字中选出不重复的个数
// @param $start 数字的开始值
// @param $end 数字的结束值
// @param $count 选出的个数
public function rand($start, $end, $count) {
$tmp = range($start, $end);
$tmp = array_rand($tmp, $count);
return $tmp;
} // 从每组汉字(一组6个)中选出n(4)个
// @param $array 二维数组
// @param $key 数组 $array 的下标
// @param $n 选出几个
public function choose($array, $key, $n) {
$arr = $array[$key];
$count = count($arr);
$tmp_key = $this->rand(0, $count - 1, $n);
$chars = array();
foreach($tmp_key as $val) {
$chars[] = $arr[$val];
}
return $chars;
} // 记录点击次数,如果次数达到4次就做出判断,验证码输入是否正确
public function count_ckick() { session_start(); // 坐标数组
$codes = $_SESSION['code_area_array']; $xy = array(
'line1_y'=>array(
'x1'=>0,
'x2'=>1,
'x3'=>2,
'x4'=>3,
),
'line2_y'=>array(
'x1'=>4,
'x2'=>5,
'x3'=>6,
'x4'=>7,
),
'line3_y'=>array(
'x1'=>8,
'x2'=>9,
'x3'=>10,
'x4'=>11,
)
); if(! isset($_POST['clear']) || $_POST['clear'] != 1) {
$_SESSION['count'] = $count = $_POST['count']; if($count > 4) {
$_SESSION['count'] = 4;
} else {
// 记录选择的验证码文字
$x = $_POST['x'];
$y = $_POST['y']; foreach($xy as $key => $val) {
foreach($val as $k => $v) {
if($y == $key) {
if($x == $k) {
$code_key = $codes[$v];
}
}
}
}
}
if(! isset($_SESSION['input_code'])) {
$_SESSION['input_code'] = $code_key;
} else {
$_SESSION['input_code'] .= $code_key;
} $return = '点击了 '.$_SESSION['count'].' 次, 选中的汉字是: '.$code_key.' 输入的验证码是: '.$_SESSION['input_code']; if($count == 4) {
$code = $this->check_verify($_SESSION['input_code']);
if($code == 200) {
$return .= ' 输入正确';
} else {
$return .= ' 输入错误';
}
}
echo $return;
} else { // 清除点击次数
$_SESSION['count'] = 0;
unset($_SESSION['input_code']);
echo '成功清除了点击次数,点击次数为',$_SESSION['count'],'次';
}
} // 获取session中记录的点击次数
public function record_click() {
session_start();
if(! isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
}
echo $_SESSION['count'];
} // 修改点击记录数
public function update_click() {
session_start();
if(! isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
} else {
$newcount = $_SESSION['count'] + $_POST['times'];
if($newcount < 0) {
$_SESSION['count'] = 0;
unset($_SEIION['input_code']);
} else {
$_SESSION['count'] = $newcount;
$_SESSION['input_code'] = mb_substr($_SESSION['input_code'], 0, -1, 'utf-8');
}
}
echo '点击数是:'.$_SESSION['count'].' 验证码是:'.$_SESSION['input_code'];
}
}

  

视图 /Applicable/Home/View/PostBarVerify/index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#verify_area {
width:600px;
height: 400px;
position: relative;
padding:10px;
}
h1 {
font-size:16px;
font-family: "微软雅黑";
color: #999;
text-indent: 30px;
}
#notice {
position: relative;
top: 95px;
left: 30px;
color: #666;
display: block;
z-index: 2;
}
#buttons {
width: 350px;
height: 150px;
position: relative;
top: 110px;
left: 20px;
z-index: 2;
padding: 0;
}
#verify_pic {
position: relative;
top: -150px;
}
.button {
display: inline-block;
cursor: pointer;
margin: -15px 12px 15px 5px;
width: 60px;
height: 45px;
border: 1px solid #E0E0E0;
border-bottom-color: #BFBFBF;
outline: 0;
background: -ms-linear-gradient(top,#fff,#f5f5f5);
background: -webkit-gradient(linear,left top,left bottom,from(#fff),to(#f5f5f5));
background: -moz-linear-gradient(top,#fff,#fafafa);
filter: progid:DXImageTransform.Microsoft.Gradient(gradientType=0, startColorStr=#FFFFFF, endColorStr=#F5F5F5);
-webkit-opacity: 0.3;
-moz-opacity: 0.3;
-khtml-opacity: 0.3;
opacity: .3;
filter:alpha(opacity=30);
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=30);
zoom: 1;
}
#verify_title {
text-indent: 30px;
position: relative;
top: 25px;
font-family: "微软雅黑";
color: #999;
}
#verify {
width: 200px;
height: 34px;
position: relative;
top: 0px;
left: 80px;
border: 1px solid #ccc;
}
.verify {
width: 40px;
height: 34px;
border-right: 1px solid #ccc;
float: left;
background-repeat: no-repeat;
}
.verify_last { border-right: 0 }
.cls { clear: both }
.hid { display: none; }
#delete {
position: relative;
left: 162px;
width: 39px;
height: 34px;
background: url('__PUBLIC__/Images/delete.png') 0 0 no-repeat;
cursor: pointer;
}
.addbg {
background-image: url("__PUBLIC__/Images/verifyImage.png")
}
</style>
<script src="__PUBLIC__/Js/jquery-1.9.1.min.js"></script>
</head>
<body>
<div id="verify_area">
<form action="{:U('Home/PostBarVerify/check_verify','','')}" method="post" id="form">
<h1>点击验证码图片换一张</h1>
<div>
<div id="verify_title">验证码</div>
<div id="verify">
<div id="verify1" class="verify"></div>
<div id="verify2" class="verify"></div>
<div id="verify3" class="verify"></div>
<div id="verify4" class="verify verify_last"></div>
<div id="delete"></div>
<div class="cls"></div>
</div>
</div>
<p id="notice">点击框内文字输入上图中汉字</p>
<div id="buttons">
<for start="0" end="3" name="i">
<br />
<for start="0" end="4" name="j">
<div class="button" x="x{$j+1}" y="line{$i+1}_y"></div>
</for>
</for>
</div>
<img id="verify_pic" src='' style="cursor: pointer;" alt="">
<span class="hid" id="verify_url">{:U('Home/PostBarVerify/verify','','')}</span>
</form>
</div>
</body>
<script>
$(function(){ // 加载或刷新时清空session计数
$count = 0;
clear_count(); $xy = {
"line1_y" : -112, // 备选验证码第一行y坐标
"line2_y" : -165, // 备选验证码第二行y坐标
"line3_y" : -212, // 备选验证码第三行y坐标
"x1" : -35, // 备选验证码每行第一个字x坐标
"x2" : -114, // 备选验证码每行第二个字x坐标
"x3" : -195, // 备选验证码每行第三个字x坐标
"x4" : -276 // 备选验证码每行第四个字x坐标
}; $verify_url = $("#verify_url").html(); // 验证码请求地址
$src = $verify_url + '?' + Math.random();
change_verify($src); // 载入页面时加载验证码 $("#verify_pic").click(function(){ // 点击验证码时更换验证码
change_verify($src);
// 清除验证码
$(".verify").css('background-image', '');
clear_count($count);
}); function clear_count() {
// 清空session计数
$.ajax({
url: '{:U("/Home/PostBarVerify/count_ckick")}',
type: 'post',
data: {
"clear": 1
},
success: function(data) {
console.log(data);
}
});
} // 更换验证码
function change_verify($src) { var flag = 0;
// 请求验证码地址生成验证码图片
$.ajax({
url: $src,
async: false,
success: function() {
flag = 1;
}
}); if(flag == 1) {
$('#verify_pic').attr('src', $src); // 验证码图片图片
}
} // 点击备选区选择验证码
$('.button').click(function() { // 查询session中保存的count
$.ajax({
url: '{:U("/Home/PostBarVerify/record_click")}',
async: false,
success: function(data) {
$count = data;
}
}); $count++;
if($count > 4) {
// return false;
} $.ajax({ // 记录点击次数以及点击的文字
url: '{:U("/Home/PostBarVerify/count_ckick")}',
type: 'post',
data: {
"count": $count,
"x": $(this).attr("x"),
"y": $(this).attr("y")
},
success: function(data) {
if(data != '') {
console.log(data);
}
}
}); $x = $(this).attr('x');
$y = $(this).attr('y'); choose_verify($xy, $x, $y, $count);
}); // 生成每次点击验证码的坐标,填充到验证码
function choose_verify($xy, $x, $y) { $x = $xy[$x] + 'px';
$y = $xy[$y] + 'px'; // 填充验证码
$('#verify'+$count).css({
'background-position-x': $x,
'background-position-y': $y
}).addClass('addbg');
} // 删除验证码
$('#delete').click(function(){ $.ajax({
url: '{:U("Home/PostBarVerify/record_click")}',
type: 'post',
async: false,
success: function(data) {
if(data == 0) {data = 1;}
$i = data;
}
}); $('#verify'+$i).removeClass('addbg');console.log('#verify'+$i);
$i--;
// 修改计数,清除session
$.ajax({
url: '{:U("Home/PostBarVerify/update_click")}',
type: 'post',
data: {
'times': -1
},
success: function(data) {
console.log(data);
}
});
$count--;
});
});
</script>
</html>

  

演示

初始状态:

输入验证码:

输入完成,返回结果,错误时:

输入完成,返回结果,正确时:

删除验证码:

刷新验证码:

参考:

PHP生成不重复随机数的方法汇总

CSS透明opacity和IE各版本透明度滤镜filter的最准确用法

最新文章

  1. text-transform设置单词首字母大写
  2. Greenplum 生成加分区语句
  3. winform 指定浏览器打开链接
  4. HDFS API 文件读写代码演示
  5. iOS完整App资源收集
  6. Tsinsen A1516. fx 数位dp
  7. Foundation与coreFoundation的相互转换
  8. Bison executable not found in PATH by mysql install
  9. yii2 添加模块过程
  10. nginx fastcgi 自定义错误页面
  11. java的几种对象解释
  12. [翻译]Python List Comprehensions: Explained Visually || Python列表解析式
  13. 动态网页获取ajax,post方法,url里面不直接显示参数
  14. JS中将json字符串转为json对象的三种方式
  15. 异常:fatal: unable to access &#39;https://git.oschina.net/pcmpcs/library.git/&#39;: Could not resolve host
  16. 【BZOJ5316】[JSOI2018]绝地反击(网络流,计算几何,二分)
  17. Threading.local
  18. 添加RPMfusion仓库
  19. [转]手把手教你--Bootstrap Table表格插件及数据导出(可导出Excel2003及Exce2007)
  20. [js]DOM 篇

热门文章

  1. python之路八
  2. 正则表达式工具RegexBuddy使用教程
  3. Android编程容易犯的错误之二
  4. 解决NetBeans 8.x的字体问题
  5. tp5 中 model 的新增方法
  6. .Net自带缓存Cache的使用
  7. iOS-OC-基本控件之UIPageControl
  8. MVC自动绑定整数数组
  9. ASP.Net 在Update Panel局部刷新后 重新绑定JS方法
  10. GeoJSON格式规范说明