备注:
源码中是 h5 和 后端的,如果前端为小程序。无法直接使用
之前本身是验证码验证,但是要改成滑块验证
使用的框架是 yii2 源码并不是线上代码。
以下为实际代码
需要源码中的 bg 目录 img 目录 TnCode.php
源码中验证使用的是session 来校验验证码是否正确。我这边前端是小程序无法存session改用框架自带的缓存。
修改Tncode.php 类如下。。 以用户id为唯一值,验证通过后清除。
备注:
_imgout 这个方法中的 imagepng 也可以保存图片,配合控制器返回图片链接。考虑到可能会生成很多,最后还是直接输出了
<?php
namespace app\components\common;
use Yii;
/*! tncode 1.2 author:weiyingbin email:277612909@qq.com
*/
class TnCode
{
var $im = null;
var $im_fullbg = null;
var $im_bg = null;
var $im_slide = null;
var $bg_width = 240;
var $bg_height = 150;
var $mark_width = 50;
var $mark_height = 50;
var $bg_num = 6;
var $_x = 0;
var $_y = 0;
var $cache = null;
//容错象素 越大体验越好,越小破解难道越高
var $_fault = 3;
function __construct(){
//ini_set('display_errors','On');
//
// error_reporting(0);
// if(!isset($_SESSION)){
// session_start();
// }
$this->cache = Yii::$app->getCache();
}
function make($user_id){
$this->_init($user_id);
$this->_createSlide(); //创建滑动图
$this->_createBg(); //创建背景图
$this->_merge(); //合并图
$this->_imgout(); //输出图
// $imgName = $this->_imgout();
$this->_destroy(); //注销
// return $imgName;
}
function check($offset='',$user_id=''){
// if(!$_SESSION['tncode_r']){
// return false;
// }
// if(!$offset){
// $offset = $_REQUEST['tn_r'];
// }
// $ret = abs($_SESSION['tncode_r']-$offset)<=$this->_fault;
// if($ret){
// unset($_SESSION['tncode_r']);
// }else{
// $_SESSION['tncode_err']++;
// if($_SESSION['tncode_err']>10){//错误10次必须刷新
// unset($_SESSION['tncode_r']);
// }
// }
// return $ret;
if(!$this->cache->get($user_id)){
return false;
}
if(!$offset){
return false;
}
$ret = abs($this->cache->get($user_id)-$offset)<=$this->_fault;
if($ret){
$this->cache->set($user_id,'');
}else{
$num = $this->cache->get($user_id.'err');
$this->cache->set($user_id.'err',$num+1);
if($this->cache->get($user_id.'err')>10){//错误10次必须刷新
$this->cache->set($user_id,'');
}
}
return $ret;
}
private function _init($user_id){
$bg = mt_rand(1,$this->bg_num); // 1-6随机数
$file_bg = dirname(__FILE__).'/bg/'.$bg.'.png';
$this->im_fullbg = imagecreatefrompng($file_bg); //内置php 函数编辑图片
//用于创建新的true-color图像。此函数返回给定尺寸的空白图像。
$this->im_bg = imagecreatetruecolor($this->bg_width, $this->bg_height);
imagecolortransparent($this->im_bg,null);
//用于复制图像或图像的一部分
imagecopy($this->im_bg,$this->im_fullbg,0,0,0,0,$this->bg_width, $this->bg_height);
$this->im_slide = imagecreatetruecolor($this->mark_width, $this->bg_height);
$this->_x = mt_rand(50,$this->bg_width-$this->mark_width-1);
// $_SESSION['tncode_r'] = $this->_x = mt_rand(50,$this->bg_width-$this->mark_width-1);
// $_SESSION['tncode_err'] = 0;
$this->cache->set($user_id,$this->_x);
$this->cache->set($user_id.'err',0);
$this->_y = mt_rand(0,$this->bg_height-$this->mark_height-1);
}
private function _destroy(){
imagedestroy($this->im);
imagedestroy($this->im_fullbg);
imagedestroy($this->im_bg);
imagedestroy($this->im_slide);
}
private function _imgout(){
// if(!$_GET['nowebp']&&function_exists('imagewebp')){//优先webp格式,超高压缩率
// $type = 'webp';
// $quality = 40;//图片质量 0-100
// }else{
// $type = 'png';
// $quality = 7;//图片质量 0-9
// }
$type = 'png';
$quality = 7;//图片质量 0-9
header('Content-Type: image/'.$type);
$func = "image".$type; //imagepng
$func($this->im,null,$quality);
// $name = time() . rand(1, 9999) . '.png';
// $dir = realpath('./') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR;
// if (!is_dir($dir)) {
// mkdir($dir, 0777, true);
// }
// $func($this->im,$dir.$name);
// return $name;
}
private function _merge(){
$this->im = imagecreatetruecolor($this->bg_width, $this->bg_height*3);
imagecopy($this->im, $this->im_bg,0, 0 , 0, 0, $this->bg_width, $this->bg_height);
imagecopy($this->im, $this->im_slide,0, $this->bg_height , 0, 0, $this->mark_width, $this->bg_height);
imagecopy($this->im, $this->im_fullbg,0, $this->bg_height*2 , 0, 0, $this->bg_width, $this->bg_height);
imagecolortransparent($this->im, 0);//16777215
}
private function _createBg(){
$file_mark = dirname(__FILE__).'/img/mark.png';
$im = imagecreatefrompng($file_mark);
header('Content-Type: image/png');
//imagealphablending( $im, true);
imagecolortransparent($im,0);//16777215
//imagepng($im);exit;
imagecopy($this->im_bg, $im, $this->_x, $this->_y , 0 , 0 , $this->mark_width, $this->mark_height);
imagedestroy($im);
}
private function _createSlide(){
$file_mark = dirname(__FILE__).'/img/mark2.png';
$img_mark = imagecreatefrompng($file_mark);//内置php 函数编辑图片
imagecopy($this->im_slide, $this->im_fullbg,0, $this->_y , $this->_x, $this->_y, $this->mark_width, $this->mark_height);
imagecopy($this->im_slide, $img_mark,0, $this->_y , 0, 0, $this->mark_width, $this->mark_height);
imagecolortransparent($this->im_slide,0 );//16777215
//header('Content-Type: image/png');
//imagepng($this->im_slide);exit;
imagedestroy($img_mark);
}
}
?>
控制器调用
//生成验证码
public function actionSlider()
{
$tnCode = new TnCode();
$imgName = $tnCode->make($this->getUserId());
// return $this->successJson($_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].'/uploads/images/'.$imgName);
}
效果图
验证是否正确
$tn = new TnCode();
$code_s = Yii::$app->request->post('code');
if(empty($code_s)){
return $this->errorJson("缺少验证码");
}
if(!$tn->check($code_s,$this->getUserId())){
return $this->errorJson("验证错误");
}
源码在我主页,5积分下载。
小程序代码:微信小程序中使用拼图滑块儿 – 反方向的钟
优化:虽然后台有10次验证的限制。但是为了保险起见多加了一层
前端滑块轨迹数据发到后台。后台取倒数第二个数据减去倒数第一个数据除以倒数第一个数据的值之后再校验验证码是否正确
代码如下 控制器
$tn = new TnCode();
$code_str = Yii::$app->request->post('code');
$code_s = explode(',',$code_str);
if(empty($code_s) || count($code_s) < 30){
return $this->errorJson("验证失败");
}
$calculation = ( ($code_s[count($code_s) -2]) - ($code_s[0]) ) / $code_s[count($code_s) -1] ;
if(!$tn->check($calculation,$this->getUserId())){
return $this->errorJson("验证错误");
}