腾讯COS - 使用临时密钥下载私有桶对象失败Cos Error Code: AccessDenied【踩坑】

一、 获取临时密钥的请求里,权限列表里需要添加下载权限。php实例代码: $config = [ 'url' => self::URL, 'domain' => self::DOMAIN, 'proxy' => '', 'secretId' => $cos['secret_id'], // 固定密钥 'secretKey' => $cos['secret_key'], // 固定密钥 'bucket' => $bucket, // 换成你的 bucket 'region' => $region, // 换成 bucket 所在园区 'durationSeconds' => self::EXPIRE_TIME, // 密钥有效期 'allowPrefix' => '*', // 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的目录,例子:* 或者 a/* 或者 a.jpg // 密钥的权限列表。简单上传和分片需要以下的权限, 'allowActions' => array ( // 简单上传 'name/cos:PutObject', 'name/cos:PostObject', // 分片上传 'name/cos:InitiateMultipartUpload', 'name/cos:ListMultipartUploads', 'name/cos:ListParts', 'name/cos:UploadPart', 'name/cos:CompleteMultipartUpload', // 下载 'name/cos:GetObject', ) ]; // 获取临时密钥,计算签名 $tempKeys = $this->sts->getTempKeys($config); 二、确保用于生成临时密钥的账号在所在的桶有相应的权限 https://console.cloud.tencent.com/cos5/bucket 我踩的坑是因为运维给到我的账号是子账号,但是没有权限。使用该子账号的密钥生成的临时密钥能够进行上传操作,但是下载对象返回403。这就很懵逼了。 提了工单给腾讯云,折腾几回合无果后,客服让我在桶所在的权限控制台页面截图看看,说如果是子账号的话,也是需要将子账号及相应权限添加到对应桶的。 让运维将子账号添加到所在桶,并勾选上传、下载数据权限后,成功下载。

2019-09-30 · 1 分钟 · 83 字

用php对象,简化Json Schema的定义

Github地址 使用php对象,描述Json-Schema变得更容易(支持draft-07 和 draft-06) 使用opis/json-schema作为校验器 好处 定义容易,不用写复杂的json 定义更有可读性 安装 composer require "foamzou/easy-json-schema" 基本使用 use Foamzou\EasyJsonSchema\Manager\Validator; use Foamzou\EasyJsonSchema\Manager\Parser; use Foamzou\EasyJsonSchema\Type\{ Str, Integer }; // 定义一个schema $schema = new Obj([ 'name' => (new Integer)->min(2)->max(5), 'age' => (new Str)->max(120), ]); $data = [ 'name' => 'foam', 'age' => 18, ]; // 生成json-schema $jsonSchema = Parser::run($schema); //校验数据合法性,如果校验失败,errorMessage将返回具体信息 $bool = Validator::getInstance()->isValid($jsonSchema, $data, $errorMessage); Schema的定义 下面是与json-schema的对比,可以看到使用php对象的定义会更简单、更有可读性且更容易维护 php版 ...

2019-07-28 · 2 分钟 · 416 字

编译安装php7.3

最近在一台CentOS6.2的机器上部署php7.3,系统有点旧,稍微折腾点。 安装需要的依赖 sudo yum install libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel libjpeg libpng freetype libjpeg-devel libpng-devel freetype-devel gmp-devel readline-devel libxslt-devel libmemcached libmemcached-devel -y 编译安装 到官网下载最新的稳定版本 注意用户组最好与nginx的保持一致,例如我这里设置与nginx一致的nobody ./configure --prefix=/usr/local/php7 --with-config-file-path=/usr/local/php7/etc --enable-fpm --with-fpm-user=nobody --with-fpm-group=nobody --enable-inline-optimization --disable-debug --disable-rpath --enable-shared --enable-soap --with-libxml-dir --with-xmlrpc --with-openssl --with-mhash --with-pcre-regex --with-sqlite3 --with-zlib --enable-bcmath --with-iconv --with-bz2 --enable-calendar --with-curl=/usr/local/include/curl/ --with-cdb --enable-dom --enable-exif --enable-fileinfo --enable-filter --with-pcre-dir --enable-ftp --with-gd --with-openssl-dir --with-jpeg-dir --with-png-dir --with-zlib-dir --with-freetype-dir --enable-gd-jis-conv --with-gettext --with-gmp --with-mhash --enable-json --enable-mbstring --enable-mbregex --enable-mbregex-backtrack --with-onig --enable-pdo --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-zlib-dir --with-pdo-sqlite --with-readline --enable-session --enable-shmop --enable-simplexml --enable-sockets --enable-sysvmsg --enable-sysvsem --enable-sysvshm --enable-wddx --with-libxml-dir --with-xsl --enable-zip --enable-mysqlnd-compression-support --with-pear --enable-opcache make sudo make install 编译期间遇到的问题汇总 checking for cURL 7.15.5 or greater… configure: error: cURL version 7.15.5 or later is required to compile php with cURL support 实际上我本地已经安装了最新的版本。–with-curl不指定路径的话,会在/usr/include/curl下找头文件。但我本机已有的libcurl貌似没有这些头文件。尝试`yum install curl curl-devel`,报了一个多版本冲突的问题。所以最后还是从curl官网下载了最新的tar包,手动编译安装后,--with-curl指定路径为/usr/local/include/curl/ ...

2019-03-28 · 1 分钟 · 189 字

编译protobuf.so扩展

在官网下载适合php版本的包。pecl官网 解压 tar zxvf protobuf.tar.gz cd到目录并用phpize生成configure文件 cd protobuf-3.7.1/(如果是从github下载的,则cd到protobuf-3.7.1/php/ext/google/protobuf) phpize 编译三板斧 ./configure --with-php-config=/usr/local/bin/php-config make sudo make install 最后会提示你已经装到指定扩展目录了 Installing shared extensions: /usr/local/php7/lib/php/extensions/no-debug-non-zts-20180731/ 其它扩展编译也是一样的方式:参考 /posts/compiled-php-extension-in-linux/

2019-03-28 · 1 分钟 · 23 字

设计模式之职责链

什么是职责链 职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 例子一:支付宝的花呗自动还款扣款顺序 余额->借记卡快捷->余额宝 首先会从余额扣款,若余额不足或扣款失败则将扣款这一动作往后传递~ 例子二:备胎 母亲:闺女,你们也不小了。赶紧给我找个女婿,具体怎么找,你们自己决定。 大女儿:妈,别急,我正在找呢,我要求不高,对我好就行了。 小女儿:妈,我要求也不高,对我好也就可以啦~ 女生说要求不高,往往要求很高~ 哼.jpg。但女生说自己懒,往往就是真的懒。这不,找上我这个程序员了。 大小女儿:foam,你是学计算机的,除了修电脑,帮忙找备胎也是会的哈~ foam:说吧,什么要求 大女儿:我要求不高,只要对我好就可以了。但,有钱很重要~ 小女儿:我和姐姐一样,找个对我好的就行。但,长得帅很重要~ 大小女儿:还有其他不是要求的要求嘛,长得高的不能太瘦,比较矮的又不能太胖;理科生的话最好会点音乐,文科生则需要懂点修电脑 foam:(转身吐血)你们还会改需求吗~ 大小女儿:不会的 我知道,女生说不会,就是会! 于是我放弃了if…else的写法。 首先将备胎对象化是必须的。重要的是,给这些备胎串成一条链。每个节点,只要满足基本条件就返回给客户,否则往下传递,直到找到符合条件的备胎为止。 但,备胎的排序也是有条件的。大女儿说,最好有钱,那先将备胎们按身家排序,最有钱的排在链头,最穷的在链尾。小女儿的颜值要求也如法炮制。 用php简单实现的职责链demo <?php abstract class Handler { private $successor = null; final public function append(Handler $handler) { if (is_null($this->successor)) { $this->successor = $handler; } else { $this->successor->append($handler); } } final public function handle() { $processed = $this->processing(); if (!$processed) { if (!is_null($this->successor)) { $processed = $this->successor->handle(); } } return $processed; } abstract protected function processing(); } class AHandler extends Handler { public function processing() { echo 'A processing '; if (满足要求) { return true; } return false; } } class BHandler extends Handler { public function processing() { echo 'B processing '; if (满足要求) { return true; } return false; } } class CHandler extends Handler { public function processing() { echo 'C processing '; if (满足要求) { return true; } return false; } } class Client { private $_chain; public function init() { //例子一(花呗扣款): $this->_chain = new AHandler(); $this->_chain->append(new BHandler); $this->_chain->append(new CHandler); //例子二(备胎): $hanlerList = getManOrderByRich(); $this->_chain = new $handlerList[0]; for ($i=1;$i<count($handlerList),++$i) { $this->_chain->append(new $handlerList[$i]); } } public function doIt() { $this->_chain->handle(); } } $client = new Client; $client->init(); $client->doIt(); 对了,后来过了几天,大女儿来找我了。 大女儿:foam,我觉得钱不是最重要的,身高才是 foam:我知道了,你等我几分钟 …… 于是我这么改了下代码 ...

2016-06-10 · 2 分钟 · 218 字

App用户身份验证

安全问题 现在的移动互联网应用,大多是通过客户端带上数据请求不同动作的api来实现不同业务要求的。即是说,通过抓包就可以获取到请求的api和发送、接收到的数据。如果没有一定的安全防范意识,很容易被攻击。 这里推荐一款叫fiddler的抓包工具,使用方法自行搜索。下面是对车来了App的抓包截图: 没有使用https,所以数据完全暴露出来了。可以看到我经常坐的19路其中一辆公交还有12分钟到站。 用户身份验证 Web应用可以使用session来保存用户会话,App可没有这东西。 下面是一个很致命的设计: 只要带上userId,服务端就会为该用户执行操作。就是说任何人都可以假冒123这个用户执行一些操作。 嗯,打死都不承认上面这种设计我曾经写过。 其实参考session的实现原理就可以解决这个问题。大多数情况,Session都是结合客户端的Cookie来实现的。 Web中Session的实现: 1.用户的登录名、密码校验正确后,我们通常会将用户的一些信息存入到Session中。 2.此时服务端会生成一个全局唯一标识SessionID来标记这个用户生成的Session,并将该SessionID通过Set-Cookie回应给浏览器。 3.So,浏览器访问该网站的其他页面时只要带上了这个SessionID,服务端就知道它面对的用户存储在了哪个Session中,程序便可取出来用。 如何实现 那么移动端App怎么玩?与Web的Session是一样的,只不过我们要自己实现了。 简单实现 1.用户名密码校验无误后,生成UUID,存储到该用户记录中对应的字段中 2.将该UUID返回给前端(Android、iOS等) 3.App请求其他需要用户登录的接口时,带上该字段 4.服务端校验无误后方可继续执行业务 进一步完善以便实现其他需求的话,可以在生成UUID的同时,记录下用户ip、设备ID、过期时间等~ 如:发现设备ID与登录时的不同,要求其重新登录~ 当然,校验技术不可能是完美的,Hacker们有太多的方法骗过服务端。作为开发者,能做的是尽可能地提高验证水平和Hacker的破解成本;而作为用户,不要总想着蹭网啦,免费或虚假Wifi的后面可能有人正抓着包…

2016-03-13 · 1 分钟 · 24 字

memcache入门

写在前面 简介 memcache是一款开源软件,由LiveJournal的Brad Fitzpatrick开发,以BSD license授权发布。 是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个巨大的hash表,将数据(包括图像、视频、文件以及数据库检索的结果等)调用到内存中,然后从内存中读取,从而大大提高读取速度。 特性 非持久性存储 定位于分布式存储,不适用于单机系统 不支持List、Array等复杂的数据 Memcache和Memcached 这款开源软件的项目名叫Memcache,Memcached (Memcached-Daemon的简称) 是软件的主程序名。 在php中,有两个为memcache开发的扩展(客户端):Memcache扩展和memcached扩展。后者比前者要新,功能也比较多。推荐安装后者。 部署环境 安装memcache服务端 sudo apt-get install memcached #for Ubuntu yum install memcached #for CentOS 安装php扩展(客户端) 这里以安装memcached扩展为例,从官网下载最新版本,解压并cd到目录 wget http://pecl.php.net/get/memcached-2.2.0.tgz tar -zxvf memcached-2.2.0.tgz cd memcached-2.2.0 编译安装 phpize #生成configure配置文件。若找不到命令请用绝对路径 /usr/local/php/bin/phpize ./configure --with-php-config=/usr/local/php/bin/php-config #配置 make #编译 make install #安装。需要root权限 安装完后会提示安装的目录: Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/ 编辑php.ini文件 #找到extension_dir,如果没有设置扩展的路径,则把上面memcached.so所在的路径粘贴进去 extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/" #找到;extension=php_XXX.dll的地方,在最下面添加上 extension=memcached.so 最后,重启web服务器和php-fpm 看看phpinfo()是否将memcached添加成功。我这里为了做测试编译了两个扩展,实际编译后者就可以。 简单使用 这里只介绍php中memcached类的使用。更多请参考手册 <?php $md = new memcached; //向服务器池添加服务器,也可以使用addServers方法一次添加多个 //public bool Memcached::addServer ( string $host , int $port [, int $weight = 0 ] ) $md->addServer('127.0.0.1',11211); //$expiration默认为0,永不超时。但即使为0,当内存不够用的时候,memcache也会将不常使用的数据删掉 //expiration不能超过60×60×24×30(30天时间的秒数);如果大于这个值,服务端会将其作为一个真实的Unix时间戳来处理而不是 自当前时间的偏移 //public bool Memcached::add ( string $key , mixed $value [, int $expiration ] ) $md->add('addItem','this is content one by add'); $md->add('addItem','this is content two by add');//false.若使用add方法添加已经存在的key,无法添加成功 //public mixed Memcached::get ( string $key [, callback $cache_cb [, float &$cas_token ]] ) echo $md->get('addItem');//after add:this is content one by add //public bool Memcached::delete ( string $key [, int $time = 0 ] ) $md->delete('addItem'); echo $md->get('addItem');//空 //使用set方法重复添加同样的key是合法的,且后面的数据会覆盖前面的数据 //public bool Memcached::set ( string $key , mixed $value [, int $expiration ] ) $md->set('tip','hello');//tip='hello' $md->set('tip','world');//tip='world' //若使用追加方法,必须将OPT_COMPRESSION(压缩)设置为false $md->setOption(Memcached::OPT_COMPRESSION,false); $md->set('tip','hello');//tip='hello' //向指定元素后面追加一个字符串 //public bool Memcached::append ( string $key , string $value ) $md->append('tip',' world');//tip='hello world' $arr = [ 'name' => 'foam', 'age' => '22', ]; //同时存储多个数据 //public bool Memcached::setMulti ( array $items [, int $expiration ] ) $md->setMulti($arr); echo $md->get('name');//foam echo $md->get('age');//22 $md->set('count','-99');//count=-99 //自增 //public int Memcached::increment ( string $key [, int $offset = 1 ] ) $md->increment('count');//false.对于负数和非整数是无法自增的 $md->set('count',0); $md->increment('count');//count=1 $md->increment('count',100);//count=101 //自减 //public int Memcached::decrement ( string $key [, int $offset = 1 ] ) $md->decrement('count');//count=100 $md->decrement('count',50);//count=50 $md->decrement('count',60);//count=0.最小只能减到0 //清空所有数据 //public bool Memcached::flush ([ int $delay = 0 ] ) $md->flush(); $md->get('tip');//false $md->get('count');//false 其他 Memcache的分布式缓存 Memcache尽管说是分布式缓存服务器,但其分布式却是由客户端实现的。 保存数据时,客户端会根据数据的键使用特定的算法(不同的客户端,算法不同)选择要保存的服务器,将其存入其中。获取数据时也是使用相同的算法在所在的服务器获取。这些客户端算法一般在Hash的基础上进行改良,保证其数据的分散性。 具体策略就不展开说明了。 ...

2016-01-20 · 2 分钟 · 416 字

Laravel5.1学习之旅(二) 路由

Laravel5.1学习之旅(二) 路由 上一篇:Laravel5.1学习之旅(一) 安装Laravel5.1 访问一个页面,你可以不需要Model、View和Controller,但必然需要路由(Route) 本文示例代码可能会涉及到Controller、Model、View,这些内容将会在下一篇介绍,对于学习本文没有影响 路由是什么 学过计算机网络的都知道,路由负责将接收到的数据包拆解出目标ip地址等信息,然后根据路由表进行分组转发给下一个目标。 Web框架中的路由也是如此,它将请求中的URL拆解为控制器、方法、参数等信息,然后调用相应的控制器文件来处理业务,最后将带有数据的视图呈现给用户。当然,Laravel显得更为简单粗暴,它直接提供了一个路由表app/Http/route.php,你可以在该文件定制你的路由规则。这样你的路由非常直观且灵活,代价就是当你要添加一个新的请求时必须要在该文件添加一条新的路由规则。 最简单的路由 废话懒得说,直接上代码: Route::get('/',function(){ return 'Hello foam'; }); 可以看到,上面的Route::get需要传递两个参数,第一个是URL,第二个是闭包方法。因为是闭包方法,你可以在里面写业务逻辑,这里我们只简单返回一个Hello foam。 在浏览器中输入http://foam.com(网站根目录),页面会打印出Hello foam。 基础路由 基本路由: <?php //以get方法访问http://foam.com/home时将执行HomeController里的index方法 Route::get('home','HomeController@index'); //除此之外还有post、put、delete等方式。一般用得比较多的是get和post Route::post('home','HomeController@index'); Route::put('home','HomeController@index'); Route::delete('home','HomeController@index'); 带有参数的路由: 很多场景的url都是带有参数的,例如文章详细页http://foam.com/article/9434 Route::get('article/{id}','ArticleController@detail'); 如此一来,ArticleController的detail方法便能接收到id参数,具体方法我们下一篇介绍 路由群组: 路由群组一方面将一组相关路由写在一块,提高了代码可读性;另一方面简化了代码,可利用中间件统一处理相关代码 举个例子,假如我们需要写个后台管理,URL为http://foam.com/backend/***/,命名空间为Admin,且需要使用中间件AdminAuth对后台管理系统的所有请求作权限验证。 没有群组,需要这么写: Route::get('backend/aaaaa', ['middleware' => 'AdminAuth', 'Admin\AaaController@index']); Route::get('backend/bbbbb', ['middleware' => 'AdminAuth', 'Admin\BbbController@index']); Route::get('backend/ccccc', ['middleware' => 'AdminAuth', 'Admin\CccController@index']); 现在让我们的群组重新演绎: Route::group(['prefix'=>'backend','namespace'=>'Admin','middleware'=>'AdminAuth'],function(){ Route::get('/aaaaa','AaaController@index'); Route::get('/bbbbb','BbbController@index'); Route::get('/ccccc','CccController@index'); }); 群组里prefix是URL前缀,namespace是命名空间,middleware是中间件,这些都是可选的。 群组也是可以嵌套的,当部分页面需要同一个中间件处理时,可以这么做: Route::group(['namespace'=>'Home'],function(){ Route::get('/', 'PageController@index'); Route::get('task/{id}','TaskController@detail'); Route::group(['middleware'=>'UserAuth'],function(){ Route::get('myhome','MyhomeController@index'); Route::get('myhome/taskDetail/{id}','MyhomeController@taskDetail'); }); });

2015-09-12 · 1 分钟 · 69 字

在Linux下编译php扩展

在windows下我们需要开启某个扩展,很容易。一般情况下,dll文件都已经安静地躺在extension目录下,只等待你在php.ini里将它前面的;去掉。 而在linux,你cd到php的extensions目录往往发现里面空空的。或许只有几个so文件落寞地待着。 这里以编译fileinfo扩展为例(注意这是php内置的扩展,如果你编译的是第三方扩展请自行到官网下载源码) 一、找到扩展包 你需要到PHP源码包里寻找,希望源码包没被你删除。 cd到php-5.x.x/ext/目录,你会发现里面有很多内置的扩展 我们cd到fileinfo目录 二、编译 phpize ./configure --with-php-config=/usr/local/php/bin/php-config make && make install 上面的命令很好理解,首先是执行phpize,它是用来安装php扩展的工具,如果你的系统无法识别,请使用完整路径。比如/usr/local/php/bin/phpize 。这个工具会在当前目录下生成configure文件。 接着就是编译的三板斧啦~其中with-php-config参数请按照你实际情况填写 没有意外的话,经过上面的编译,你会在php/lib/php/extensions/(在php.ini的extension_dir可找到, 或者 php -i | grep exten)目录下找到生成的fileinfo.so文件 三、添加配置 最后只需要在php.ini里把扩展添加进去就好了 extension = "fileinfo.so" 最后别忘了reload php-fpm

2015-08-09 · 1 分钟 · 32 字

PDO学习 step 1

PHP Data Objects(PDO)是随着PHP5.1发布开始附带的,所以不用担心你的PHP环境上是否支持PDO扩展。 该扩展默认是开启的,如果你的代码跑不起来,应该去phpinfo看看是否已经开启PDO扩展。 ThinkPHP从3.2.3版本开始,数据库类和驱动采用PDO重写了。但我在学TP之前还不知道PDO,还在用那个要被淘汰的mysql扩展。 下面是PDO常用的方法 PDO::query() 一般用于SELECT操作 PDO::exec() 一般用于INSERT、UPDATE等不需要返回记录集的操作 PDO::prepare() 预处理操作,需要通过创建的statement->execute()来执行经过预处理的SQL语句 PDO::lastInsertId() 返回上次插入的自增ID PDOStatement::execute() 执行通过PDO::prepare()预处理过的SQL语句 PDOStatement::fetch() 返回一条记录 PDOStatement::fetchAll() 返回一个包含结果集中所有行的数组 PDOStatement::fetchColumn() 返回记录集中第一行的第一个字段 PDOStatement::rowCount() 返回受 DELETE、INSERT、 或 UPDATE 语句影响的行数。对PDO::exec()方法和SELECT操作无效。 操作MySql的实例 <?php $pdo = new PDO("mysql:host=localhost;dbname=数据库名","账号","密码"); $rs = $pdo->query("SELECT * FROM table"); foreach($rs as $row) { $users[] = $row; } $stmt = $pdo->prepare("INSERT INTO table VALUES(:name,:sex,:age)"); $stmt->bindParam(":name",$name); $stmt->bindParam(":sex",$sex); $stmt->bindParam(":age",$age); $stmt->execute(); ?>

2015-04-01 · 1 分钟 · 59 字