From d1875fd32e0aa7a093544b5decd0b43499424d4f Mon Sep 17 00:00:00 2001 From: xue <> Date: Thu, 30 Aug 2007 15:40:24 +0000 Subject: further enhanced TCaptcha. --- .../source/prado/validator/validation3.js | 13 +-- framework/Web/UI/WebControls/TCaptcha.php | 107 ++++++++++++++++++--- framework/Web/UI/WebControls/TCaptchaValidator.php | 11 ++- framework/Web/UI/WebControls/assets/captcha.php | 4 + 4 files changed, 114 insertions(+), 21 deletions(-) (limited to 'framework/Web') diff --git a/framework/Web/Javascripts/source/prado/validator/validation3.js b/framework/Web/Javascripts/source/prado/validator/validation3.js index 3d43a5f3..62d6e718 100644 --- a/framework/Web/Javascripts/source/prado/validator/validation3.js +++ b/framework/Web/Javascripts/source/prado/validator/validation3.js @@ -1388,14 +1388,11 @@ Prado.WebUI.TCaptchaValidator = Class.extend(Prado.WebUI.TBaseValidator, */ evaluateIsValid : function() { - var a = this.getValidationValue(); - if (a.length <= 0) - return false; - var b = this.options.TokenHash; - if (this.options.CaseSensitive) - return(this.crc32(a) == b); - else - return(this.crc32(a.toUpperCase()) == b); + var a = this.getValidationValue(); + var h = 0; + for(var i = a.length-1; i >= 0; --i) + h += a.charCodeAt(i); + return h == this.options.TokenHash; }, crc32 : function(str) diff --git a/framework/Web/UI/WebControls/TCaptcha.php b/framework/Web/UI/WebControls/TCaptcha.php index bff04236..48e57b9e 100644 --- a/framework/Web/UI/WebControls/TCaptcha.php +++ b/framework/Web/UI/WebControls/TCaptcha.php @@ -24,12 +24,18 @@ Prado::using('System.Web.UI.WebControls.TImage'); * generated and can be configured in several ways. To specify the length of characters * in the token, set {@link setMinTokenLength MinTokenLength} and {@link setMaxTokenLength MaxTokenLength}. * To use case-insensitive comparison and generate upper-case-only token, set {@link setCaseSensitive CaseSensitive} - * to false. More advanced users can try to set {@link setTokenAlphabet TokenAlphabet}, which + * to false. Advanced users can try to set {@link setTokenAlphabet TokenAlphabet}, which * specifies what characters can appear in tokens. * + * The validation of the token is related with two properties: {@link setTestLimit TestLimit} + * and {@link setTokenExpiry TokenExpiry}. The former specifies how many times a token can + * be tested with on the server side, and the latter says when a generated token will expire. + * * To specify the appearance of the generated token image, set {@link setTokenImageTheme TokenImageTheme} * to be an integer between 0 and 31. And to adjust the generated image size, set {@link setTokenFontSize TokenFontSize} * (you may also set {@link TWebControl::setWidth Width}, but the scaled image may not look good.) + * By setting {@link setChangingTokenBackground ChangingTokenBackground} to true, the image background + * of the token will be variating even though the token is the same during postbacks. * * Upon postback, user input can be validated by calling {@link validate()}. * The {@link TCaptchaValidator} control can also be used to do validation, which provides @@ -53,9 +59,10 @@ Prado::using('System.Web.UI.WebControls.TImage'); */ class TCaptcha extends TImage { - const MIN_TOKEN_LENGTH=4; + const MIN_TOKEN_LENGTH=2; const MAX_TOKEN_LENGTH=40; private $_privateKey; + private $_validated=false; /** * Checks the requirements needed for using TCaptcha. @@ -119,11 +126,11 @@ class TCaptcha extends TImage } /** - * @return integer the minimum length of the token. Defaults to 5. + * @return integer the minimum length of the token. Defaults to 4. */ public function getMinTokenLength() { - return $this->getViewState('MinTokenLength',5); + return $this->getViewState('MinTokenLength',4); } /** @@ -133,17 +140,17 @@ class TCaptcha extends TImage { $length=TPropertyValue::ensureInteger($value); if($length>=self::MIN_TOKEN_LENGTH && $length<=self::MAX_TOKEN_LENGTH) - $this->setViewState('MinTokenLength',$length,5); + $this->setViewState('MinTokenLength',$length,4); else throw new TConfigurationException('captcha_mintokenlength_invalid',self::MIN_TOKEN_LENGTH,self::MAX_TOKEN_LENGTH); } /** - * @return integer the maximum length of the token. Defaults to 8. + * @return integer the maximum length of the token. Defaults to 6. */ public function getMaxTokenLength() { - return $this->getViewState('MaxTokenLength',8); + return $this->getViewState('MaxTokenLength',6); } /** @@ -153,7 +160,7 @@ class TCaptcha extends TImage { $length=TPropertyValue::ensureInteger($value); if($length>=self::MIN_TOKEN_LENGTH && $length<=self::MAX_TOKEN_LENGTH) - $this->setViewState('MaxTokenLength',$length,8); + $this->setViewState('MaxTokenLength',$length,6); else throw new TConfigurationException('captcha_maxtokenlength_invalid',self::MIN_TOKEN_LENGTH,self::MAX_TOKEN_LENGTH); } @@ -192,6 +199,65 @@ class TCaptcha extends TImage $this->setViewState('TokenAlphabet',$value,'234578adefhijmnrtABDEFGHJLMNRT'); } + /** + * @return integer the number of seconds that a generated token will remain valid. Defaults to 600 seconds (10 minutes). + */ + public function getTokenExpiry() + { + return $this->getViewState('TokenExpiry',600); + } + + /** + * @param integer the number of seconds that a generated token will remain valid. A value smaller than 1 means the token will not expire. + */ + public function setTokenExpiry($value) + { + $this->setViewState('TokenExpiry',TPropertyValue::ensureInteger($value),600); + } + + /** + * @return boolean whether the background of the token image should be variated during postbacks. Defaults to false. + */ + public function getChangingTokenBackground() + { + return $this->getViewState('ChangingTokenBackground',false); + } + + /** + * @param boolean whether the background of the token image should be variated during postbacks. + */ + public function setChangingTokenBackground($value) + { + $this->setViewState('ChangingTokenBackground',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return integer how many times a generated token can be tested. Defaults to 5. + */ + public function getTestLimit() + { + return $this->getViewState('TestLimit',5); + } + + /** + * @param integer how many times a generated token can be tested. For unlimited tests, set it to 0. + */ + public function setTestLimit($value) + { + $this->setViewState('TestLimit',TPropertyValue::ensureInteger($value),5); + } + + /** + * @return boolean whether the currently generated token has expired. + */ + public function getIsTokenExpired() + { + if(($expiry=$this->getTokenExpiry())>0 && ($start=$this->getViewState('TokenGenerated',0))>0) + return $expiry+$startgetToken()===($this->getCaseSensitive()?$input:strtoupper($input)); + $number=$this->getViewState('TestNumber',0); + if(!$this->_validated) + { + $this->setViewState('TestNumber',++$number); + $this->_validated=true; + } + if($this->getIsTokenExpired() || (($limit=$this->getTestLimit())>0 && $number>$limit)) + { + $this->regenerateToken(); + return false; + } + return ($this->getToken()===($this->getCaseSensitive()?$input:strtoupper($input))); } /** @@ -279,6 +356,8 @@ class TCaptcha extends TImage $this->clearViewState('TokenLength'); $this->setPublicKey(''); $this->clearViewState('TokenGenerated'); + $this->clearViewState('RandomSeed'); + $this->clearViewState('TestNumber',0); } /** @@ -288,7 +367,7 @@ class TCaptcha extends TImage public function onPreRender($param) { parent::onPreRender($param); - if(!$this->getViewState('TokenGenerated',false)) + if(!$this->getViewState('TokenGenerated',0)) { $manager=$this->getApplication()->getAssetManager(); $manager->publishFilePath($this->getFontFile()); @@ -296,7 +375,7 @@ class TCaptcha extends TImage $url.='?options='.urlencode($this->getTokenImageOptions()); $this->setImageUrl($url); - $this->setViewState('TokenGenerated',true); + $this->setViewState('TokenGenerated',time()); } } @@ -314,6 +393,12 @@ class TCaptcha extends TImage $options['alphabet']=$this->getTokenAlphabet(); $options['fontSize']=$this->getTokenFontSize(); $options['theme']=$this->getTokenImageTheme(); + if(($randomSeed=$this->getViewState('RandomSeed',0))===0) + { + $randomSeed=(int)(microtime()*1000000); + $this->setViewState('RandomSeed',$randomSeed); + } + $options['randomSeed']=$this->getChangingTokenBackground()?0:$randomSeed; $str=serialize($options); return base64_encode(md5($privateKey.$str).$str); } diff --git a/framework/Web/UI/WebControls/TCaptchaValidator.php b/framework/Web/UI/WebControls/TCaptchaValidator.php index 4385bbfe..6d2c8f4c 100644 --- a/framework/Web/UI/WebControls/TCaptchaValidator.php +++ b/framework/Web/UI/WebControls/TCaptchaValidator.php @@ -102,16 +102,23 @@ class TCaptchaValidator extends TBaseValidator $control=$this->findCaptchaControl(); if($control->getCaseSensitive()) { - $options['TokenHash']=crc32($control->getToken()); + $options['TokenHash']=$this->generateTokenHash($control->getToken()); $options['CaseSensitive']=true; } else { - $options['TokenHash']=crc32(strtoupper($control->getToken())); + $options['TokenHash']=$this->generateTokenHash(strtoupper($control->getToken())); $options['CaseSensitive']=false; } return $options; } + + private function generateTokenHash($token) + { + for($h=0,$i=strlen($token)-1;$i>=0;--$i) + $h+=ord($token[$i]); + return $h; + } } ?> \ No newline at end of file diff --git a/framework/Web/UI/WebControls/assets/captcha.php b/framework/Web/UI/WebControls/assets/captcha.php index 3da5f035..26ed44f4 100644 --- a/framework/Web/UI/WebControls/assets/captcha.php +++ b/framework/Web/UI/WebControls/assets/captcha.php @@ -37,6 +37,10 @@ if(isset($_GET['options'])) $alphabet=$options['alphabet']; $fontSize=$options['fontSize']; $theme=$options['theme']; + if(($randomSeed=$options['randomSeed'])>0) + srand($randomSeed); + else + srand((int)(microtime()*1000000)); $token=generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive); } } -- cgit v1.2.3