WordPressのプラグインで認証付きダウンローダー

S3に入れて置いたデータをwordpressのプラグインで認証付きのダウンローダを作れないかとの検討

プラグインは便利なので、Wordpressにログインした人のみがダウンロードできるようにしたい

こんな感じでできました

環境

    • s3にファイルを格納しておく
      wordpressはec2
      プラグインはショートコードでWordpressの固定ページなどに埋め込む
  • 仕様

    http://example.com/downloader?img=dir/file.png&token=session&userid=userid
    
    • img: S3のファイルパス
    • token: wordpressのセッションID
    • userid: wordpressのログインID(ログイン名ではなくWordpress内部でのログイン番号)

    データ

    以下に格納しておく
    s3://example/dir/file.png

    ショートコード

    固定ページをhttp://example.com/downloaderで作成し、以下を埋め込む

    [sample_downloader]
    

    プラグイン

    sample_downloaderディレクトリ以下にmain.phpで作成

    run($userid,$token,$img);
    }
    
    require_once( plugin_dir_path(__FILE__) . 'vendor/autoload.php' );
    add_shortcode('sample_downloader','sample_downloader_func');
    use Aws\S3\S3Client;
    
    
    class SampleDownloader{
        function run($userid,$token,$img){
            $bucket="example";
    
            // 認証
            $wp_token=wp_get_session_token();
            $wp_userid=wp_get_current_user();
            if($userid!=$wp_userid->ID || $wp_token!=$token){
                header("HTTP/1.1 401 Unauthorized");
                return "Authorized Error";
            }
    
            try{
    
                $s3Client = S3Client::factory(array(
                        'version' => 'latest',
                        'region'  => 'ap-northeast-1'
    	        ));
                
                $result = $s3Client->getObject([
                    'Bucket' => $bucket,
                    'Key' => $img
                ]);
                $body=$result["Body"];
    
    
                $fname=basename($img);
                $sfx=pathinfo($fname, PATHINFO_EXTENSION);
                header("HTTP/1.1 200 OK");
    
                // これで本体から出力されるHTMLをクリアする
    	    if (ob_get_length() > 0) {
                    ob_end_clean();
                }
    
                switch($sfx){
                    case "svg":
                        header('Content-Type: image/svg+xml');
                        break;
                    case "png":
                        header('Content-Type: image/pmg');
                        break;
                    case "jpg":
                    case "jpeg":
                        header('Content-Type: image/jpeg');
                        break;
                    case "gif":
                        header('Content-Type: image/gif');
                        break;
                    case "csv":
                        header('Content-Type: text/csv');
                        break;
                    case "json":
                        header('Content-Type: application/json');
                        break;
                    case "pdf":
                        header('Content-Type: application/pdf');
                        break;
                    case "xls":
                        header('Content-Type: application/vnd.ms-excel');
                        break;
                    case "xlsx":
                        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
                        break;
                    case "ppt":
                        header('Content-Type: application/vnd.ms-powerpoint');
                        break;
                    case "pptx":
                        header('Content-Type: application/vnd.openxmlformats-officedocument.presentationml.presentation');
                        break;
                    case "doc":
                        header('Content-Type: application/msword');
                        break;
                    case "docx":
                        header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
                        break;
                    default:
                        header('Content-Type: application/octet-stream');
                }
    
                header('Content-Disposition: attachment; filename='.$fname);
                return $body;
    
            }catch(Exception $e){
                header("HTTP/1.1 500 Internal Server Error");
    	    return "Error";
            }
        }
    }
    ?>
    
    

    デプロイ

    zipファイルを作成しWordpressのプラグインでアップロード
    cd sample_downloader
    composer  require aws/aws-sdk-php
    cd ..
    zip -r sample_downloader.zip sample_downloader/*php sample_downloader/vendor
    

    Apache+mod_mem_cacheでキャッシュしてみる

    Squidを使えば簡単にリバースプロキシを作ることができます。ただ、一つ問題がありキャッシュ時間が分単位となります。

    リアルタイム性をできるだけ持たしたいのだが、数秒間のキャッシュを行いたいときにはSquidではちょっとできそうにありません。そこでApache+mod_mem_cacheで試してみます

    参考にしたページ

    1. http://httpd.apache.org/docs/2.2/mod/mod_cache.html
    2. http://d.hatena.ne.jp/mhag/20070417/1176786290
    3. http://blog.as-is.net/2007/02/mt-searchcgi-modcache.html
    4. http://kamoland.com/wiki/wiki.cgi?mod_cache%A4%C7Wiki%28CGI%29%A4%CE%B9%E2%C2%AE%B2%BD
    5. http://d.hatena.ne.jp/rougeref/20090616

    Apache2.2.22インストール

    ./configure --enable-so --enable-ssl --with-ssl=/usr/local/ssl/lib --enable-logio --enable-proxy \
    --enable-rewrite --enable-cache --enable-mem-cache --enable-disk-cache
    make
    make install
    

    httpd.confをこんな感じで設定します設定します。

    どうやらmod_mem_cacheはメモリ上のキャッシュが変に効いているので最初はいいがそのうちダメになるので、mod_disk_cacheをつかいます

     
    	 ProxyPass ajp://localhost:8009/jsp/
     
    LoadModule perl_module modules/mod_perl.so
    
    
    		 SetHandler perl-script
    		 PerlResponseHandler ModPerl::Registry
    		 PerlOptions +ParseHeaders
    		 Options +ExecCGI
    		 Order allow,deny
    		 Allow from all
    
    
      
    	 
    		 CacheRoot /tmp
    		 CacheEnable disk /
    		 CacheEnable disk /jsp
    		 CacheEnable disk /cgi
    		 CacheDefaultExpire 5
    		 CacheMaxExpire 5
    		 CacheIgnoreCacheControl On
    		 CacheIgnoreNoLastMod On
    		 CacheIgnoreHeaders Set-Cookie
    		 CacheDirLevels 5
    		 CacheDirLength 3
    		 CacheIgnoreURLSessionIdentifiers PHPSESSIONID jsessionid
    	 
     
    

    今回はJSPとperlのCGIを対象にしますので、mod_perlも設定しておきます

    Perl

    これらの設定でJSPは簡単にキャッシュが効いてくれたのですがどうもcgiはうまくキャッシュされていない模様です。

    どうもHTTPHeaderにExpiresとCache-Control:max-ageがうまく入っていなかったようですのでcgi側で強制的に入れてやります

    #!/usr/bin/perl
    print "Content-type:text/plain\n";
    print "Expires: 5\n";
    print "Cache-Control: max-age=5\n";
    print "last-Modified:	Tue, 15 Nov 1994 12:45:26 GMT\n";
    print "\n";
    

    結局のところ

    apacheの設定

    mod_disk_cacheを使うのが吉。

    その場合、CacheDirをクローンで定期的に消す必要あり

    cgiの設定

    たぶん不要だがmod_perlを使うようにする

    perlの書き方

    HeaderにExpires:5 と Cache-Control: max-age=5 を入れておく

    なぜか勝手にApacheがmax-age=0を返していたのでこれは必須