Yii2.0是一个高效,安全,快速的PHP开发框架,内置了很多开发时常用的功能。Yii也内置了验证码功能,让我们在开发时可以直接使用,不过框架自带的验证码在生成后,无论是点击验证码还是重新请求生成验证码的URL,生成的验证码内容是没有变化的,
这样当然是不行的啊!有问题就要解决,往下看...
1.如何生成验证码
yii框架生成验证码,只需在控制器里的actions方法里,添加一些配置就行了
public function actions()
{
return [
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
//'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
'backColor' => 0x000000,//背景颜色
'maxLength' => 4, //最大显示个数
'minLength' => 4,//最少显示个数
'padding' => 7,//间距
'height' => 40,//高度
'width' => 130, //宽度
'foreColor' => 0xffffff, //字体颜色
'offset' => 4, //设置字符偏移量 有效果
],
];
}
然后再模板文件里放验证码的位置,加上
?>)
这样,在模板页面里就会生成验证码图片了
而且呢,这个验证码的验证,也是很简单,只需要在指定的模型类中的rules中指定验证类型为验证码,接收到输入数据后,validate()就可以了
public function rules()
{
return [
['verifyCode', 'captcha','captchaAction'=>'/public/captcha', 'message'=>'验证码输入错误'],
];
}
2.问题所在:点击验证码后,验证码图片及内容刷新是常规需求,但是yii内置验证码并没有实现此功能,需要我们手动去解决
一般的验证码刷新,都是在生成验证码的url后,拼接一些随机数字,日期什么的,如:/public/captcha?Math.random() .再次点击时,会重新请求这个url,验证码会随之刷新。但是这样处理后,Yii框架验证码并没有刷新,这是为什么呢?
看下源码就能知道问题在哪儿了:
yii框架验证码相关的文件在 vendor/yiisoft/yii2/captcha/CaptchaAction.php,里面有一个 run 方法负责返回生成的验证码图片
我们来分析这个方法,我直接写在注释里了
/**
* The name of the GET parameter indicating whether the CAPTCHA image should be regenerated.
*/
const REFRESH_GET_VAR = 'refresh';
/**
* Runs the action.
*/
public function run()
{
//第一步先检测,请求参数里有没有self::REFRESH_GET_VAR,而这个self::REFRESH_GET_VAR是在类里定义的一个常量,值为‘refresh’
if (Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) !== null) {
// AJAX request for regenerating code
//如果有这个refresh请求参数,getVerifyCode方法就生成一个验证码,记住此时这个方法的一个参数true
$code = $this->getVerifyCode(true);
//声明返回格式为json
Yii::$app->response->format = Response::FORMAT_JSON;
//返回数据
return [
'hash1' => $this->generateValidationHash($code),
'hash2' => $this->generateValidationHash(strtolower($code)),
// we add a random 'v' parameter so that FireFox can refresh the image
// when src attribute of image tag is changed
'url' => Url::to([$this->id, 'v' => uniqid('', true)]),//这个URL是拼接好的访问验证码图片的链接
];
}
//第二步,如果没有refresh请求参数
$this->setHttpHeaders();
Yii::$app->response->format = Response::FORMAT_RAW;
//返回验证码图片,此时的getVerifyCode方法没有任何参数
return $this->renderImage($this->getVerifyCode());
}
我们再来看下getVerifyCode()方法的源码,同样分析写在注释里
/**
* Gets the verification code.
* @param bool $regenerate whether the verification code should be regenerated.
* @return string the verification code.
*/
public function getVerifyCode($regenerate = false)
{
//上面再配置验证码时,有一个fixedVerifyCode参数,如果有给这个参数赋值了,
//相当于给验证码设置了一个固定的值
//这里就直接返回配置时的固定值
if ($this->fixedVerifyCode !== null) {
return $this->fixedVerifyCode;
}
//如果没有设定固定值
//验证码是保存到session中的
$session = Yii::$app->getSession();
$session->open();
//验证码保存到session中key值,这个key是按照访问的‘控制器名’和‘方法名’进行拼接的
$name = $this->getSessionKey();
//判断session中是不是已经有这个key值
//$regenerate,是否重新生成验证码,这个参数默认是false
//就是说,当session中不存在这个key值或$regenerate为true时,就会重新生成验证码
if ($session[$name] === null || $regenerate) {
//generateVerifyCode()是按照配置的规则,拼接出一个验证码字符串,并存到session里
$session[$name] = $this->generateVerifyCode();
$session[$name . 'count'] = 1;
}
//返回保存在session里的验证码
return $session[$name];
}
由上面的分析源码,可知,在没有refresh时,getVerifyCode()没有传参数,也就是说程序检测到如果已经在session中已经存在验证码的key,就没有重新生成验证码,而是直接返回了,这里我打印下$_SESSION,看下保存在session中验证的验证码:
而当有refresh参数时,getVerifyCode(true)有参数true,即为重新生成一个验证码的!!!
Are you clear? 这就是yii框架验证码不刷新的问题所在了,那怎么解决呢?
3.上面说到要有refresh参数时,验证码才会重新生成,所以就需要构造一个ajax请求,携带这个参数请求生成验证码的url(/public/captcha?refresh),然后把返回的新验证码URL给模板上验证码图片的src更换下。
//刷新验证码
$("#verifyCode").click(function get_code(obj){
$.ajax({
//使用ajax请求site/captcha方法,加上refresh参数,接口返回json数据
url: '/public/captcha?refresh',
dataType: 'json',
cache: false,
success: function (data) {
//将验证码图片中的图片地址更换
$("#verifyCode").attr('src', data['url']);
}
});
});
OK。
这样就可以点击切换验证码了。