PHP Token(令牌)设计


Posted in PHP onMarch 15, 2008

如何达到目的:

怎样避免重复提交?
在SESSION里要存一个数组,这个数组存放以经成功提交的token.在后台处理时,先判断这个token是否在这个数组里,如果存在,说明是重复提交. 
如何检查来路?
可选项,这个token在生成的时候,加入了当前的session_id.如果别人copy你的html(token一迸copy),在提交时,理论上token里包含的session_id不等于当前session_id,就可以判断这次提交是外部提交. 
如何匹配要执行的动作?
在token的时候,要把这个token的动作名称写进这个token里,这样,在处理的时候,把这个动作解出来进行比较就行了.
我以前写的GToken不能达到上面所说的第二条,今天修改了一下,把功能2加上了.个人感觉还行.
请大家看代码,感觉哪里有不合理的地方,还请赐教!谢谢.

加密我是找的网上的一个方法,稍作了一下修改.

GEncrypt.inc.php:

<?php  
class GEncrypt extends GSuperclass {  
    protected static function keyED($txt,$encrypt_key){     
        $encrypt_key = md5($encrypt_key);     
        $ctr=0;     
        $tmp = "";     
        for ($i=0;$i<strlen($txt);$i++){     
            if ($ctr==strlen($encrypt_key)) $ctr=0;     
            $tmp.= substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1);     
            $ctr++;     
        }     
        return $tmp;     
    }      public static function encrypt($txt,$key){     
        //$encrypt_key = md5(rand(0,32000));  
        $encrypt_key = md5(((float) date("YmdHis") + rand(10000000000000000,99999999999999999)).rand(100000,999999));  
        $ctr=0;     
        $tmp = "";     
        for ($i=0;$i<strlen($txt);$i++){  
            if ($ctr==strlen($encrypt_key)) $ctr=0;     
            $tmp.= substr($encrypt_key,$ctr,1) . (substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1));     
            $ctr++;     
        }     
        return base64_encode(self::keyED($tmp,$key));  
    }  
    public static function decrypt($txt,$key){  
        $txt = self::keyED( base64_decode($txt),$key);     
        $tmp = "";  
        for ($i=0;$i<strlen($txt);$i++){     
            $md5 = substr($txt,$i,1);     
            $i++;     
            $tmp.= (substr($txt,$i,1) ^ $md5);     
        }  
        return $tmp;  
    }      
}  
?> 

GToken.inc.php
方法:

a,granteToken 参数:formName,即动作名称,key是加密/解密 密钥.
返回一个字符串,形式是: 加密(formName:session_id)

b,isToken 参数:token 即granteToken产生的结果,formName,动作名称,fromCheck是否检查来路,如果为真,还要判断token里的session_id是否和当前的session_id一至.

c,dropToken,当成功执行一个动作后,调用这个函数,把这个token记入session里,

<?php  
/**  
* 原理:请求分配token的时候,想办法分配一个唯一的token, base64( time + rand + action)  
* 如果提交,将这个token记录,说明这个token以经使用,可以跟据它来避免重复提交。  
*  
*/  
class GToken {      /**  
     * 得到当前所有的token  
     *  
     * @return array  
     */  
    public static function getTokens(){  
        $tokens = $_SESSION[GConfig::SESSION_KEY_TOKEN ];  
        if (empty($tokens) && !is_array($tokens)) {  
            $tokens = array();  
        }  
        return $tokens;  
    }  
    /**  
     * 产生一个新的Token  
     *  
     * @param string $formName  
     * @param 加密密钥 $key  
     * @return string  
     */  
    public static function granteToken($formName,$key = GConfig::ENCRYPT_KEY ){  
        $token = GEncrypt::encrypt($formName.":".session_id(),$key);  
        return $token;  
    }  
    /**  
     * 删除token,实际是向session 的一个数组里加入一个元素,说明这个token以经使用过,以避免数据重复提交。  
     *  
     * @param string $token  
     */  
    public static function dropToken($token){  
        $tokens = self::getTokens();  
        $tokens[] = $token;  
        GSession::set(GConfig::SESSION_KEY_TOKEN ,$tokens);  
    }  
    /**  
     * 检查是否为指定的Token  
     *  
     * @param string $token    要检查的token值  
     * @param string $formName  
     * @param boolean $fromCheck 是否检查来路,如果为true,会判断token中附加的session_id是否和当前session_id一至.  
     * @param string $key 加密密钥  
     * @return boolean  
     */  
    public static function isToken($token,$formName,$fromCheck = false,$key = GConfig::ENCRYPT_KEY){  
        $tokens = self::getTokens();  
        if (in_array($token,$tokens)) //如果存在,说明是以使用过的token  
            return false;  
        $source = split(":", GEncrypt::decrypt($token,$key));  
        if($fromCheck)  
            return $source[1] == session_id() && $source[0] == $formName;  
        else  
            return $source[0] == $formName;  
    }  
}  
?> 
示例:

首先从$_POST里取出token,用isToken判断.

PHP Token(令牌)设计下载此文件这一切看着似乎是没有问题了.
如果想判断是否是执行的匹配动作,可以把isToken里的formName改一下,运行,很好,没有匹配上.证明这个成功.

是否能避免重复提交,我没有验证,太简单的逻辑了.

余下的就是判断 来路检查 是否正常工作了.
把上面的示例产生的html copy到本地的一个网页内(以达到不同的域的目的),运行,检查来路不明,没有执行动作(需要把isToken的第三个参数设为true).
把isToken的第三个参数设置为false,提交,指定的动作执行了!

好了,到此为止,不知道哪个地方是否还存在BUG,这就要在长期运用中慢慢调试修改了!

PHP 相关文章推荐
PHP 数组入门教程小结
May 20 PHP
php 生成文字png图片的代码
Apr 17 PHP
mysql_num_rows VS COUNT 效率问题分析
Apr 23 PHP
php whois查询API制作方法
Jun 23 PHP
phpMyAdmin 链接表的附加功能尚未激活问题的解决方法(已测)
Mar 27 PHP
php常用ODBC函数集(详细)
Jun 24 PHP
PHP大批量插入数据库的3种方法和速度对比
Jul 08 PHP
php的laravel框架快速集成微信登录的方法
Dec 12 PHP
laravel如何开启跨域功能示例详解
Aug 31 PHP
PHP中递归的实现实例详解
Nov 14 PHP
PHP parse_ini_file函数的应用与扩展操作示例
Jan 07 PHP
php 使用ActiveMQ发送消息,与处理消息操作示例
Feb 23 PHP
php项目打包方法
Feb 18 #PHP
PHP4与PHP5的时间格式问题
Feb 17 #PHP
PHP5 面向对象程序设计
Feb 13 #PHP
Dedecms常用函数解析
Feb 01 #PHP
用php实现批量查询清除一句话后门的代码
Jan 20 #PHP
asp和php下textarea提交大量数据发生丢失的解决方法
Jan 20 #PHP
php开发工具之vs2005图解
Jan 12 #PHP
You might like
让你的WINDOWS同时支持MYSQL4,MYSQL4.1,MYSQL5X
2006/12/06 PHP
php 5.3.5安装memcache注意事项小结
2011/04/12 PHP
PHP过滤★等特殊符号的正则
2014/01/27 PHP
jQuery中的RadioButton,input,CheckBox取值赋值实现代码
2014/02/18 PHP
Zend Framework实现将session存储在memcache中的方法
2016/03/22 PHP
让网页根据不同IE版本显示不同的内容
2009/02/08 Javascript
jquery.boxy插件的iframe扩展代码
2010/07/02 Javascript
jquery ajax 同步异步的执行 return值不能取得的解决方案
2012/01/08 Javascript
使用jquery实现图文切换效果另加特效
2013/01/20 Javascript
浏览器打开层自动缓慢展开收缩实例代码
2013/07/04 Javascript
js jquery获取随机生成id的服务器控件的三种方法
2013/07/11 Javascript
通过pjax实现无刷新翻页(兼容新版jquery)
2014/01/31 Javascript
JQuery做的一个简单的点灯游戏分享
2014/07/16 Javascript
jQuery网页选项卡插件rTabs用法实例分析
2015/08/26 Javascript
js 性能优化之算法和流程控制
2017/02/15 Javascript
JS获取短信验证码倒计时的实现代码
2017/05/22 Javascript
node 命令方式启动修改端口的方法
2018/05/12 Javascript
详解Angular-ui-BootStrap组件的解释以及使用
2018/07/13 Javascript
axios 封装上传文件的请求方法
2018/09/26 Javascript
layer.prompt使文本框为空的情况下也能点击确定的方法
2019/09/24 Javascript
vue中beforeRouteLeave实现页面回退不刷新的示例代码
2019/11/01 Javascript
Vue移动端用淘宝弹性布局lib-flexible插件做适配的方法
2020/05/26 Javascript
Python subprocess库的使用详解
2018/10/26 Python
python文档字符串(函数使用说明)使用详解
2019/07/30 Python
用Python绘制漫步图实例讲解
2020/02/26 Python
python实例化对象的具体方法
2020/06/17 Python
Python Flask异步发送邮件实现方法解析
2020/08/01 Python
REN Clean Skincare官网:英国本土有机护肤品牌
2019/02/23 全球购物
美国探亲签证邀请信
2014/02/05 职场文书
《中华少年》教学反思
2014/02/15 职场文书
室内设计专业毕业生求职信
2014/05/02 职场文书
机械工程及其自动化专业求职信
2014/08/08 职场文书
项目负责人岗位职责
2015/02/15 职场文书
节约用电倡议书
2015/04/28 职场文书
论文致谢词范文
2015/05/14 职场文书
Python实现GIF动图以及视频卡通化详解
2021/12/06 Python