[Perl] OAuth 認証を 0 から書いてみた

Posted by
ぴろり
Posted at
2010/12/31 14:01
Trackbacks
関連記事 (0)
Comments
コメント (1)
Post Comment
コメントできます
Category
開発メモ カテゴリ
カバーイメージ

 Perl で twitter のボットを作ろうとして少し調べてみたんですが、ネット上の情報では、その多くが Net::Twitter や Net::Twitter::Lite の CPAN モジュールを利用しています。これらのモジュールは他に依存するモジュールが多くて、レンタルサーバ上にインストールして動作させるにはちょっと厳しい感じです。仕方なく英語ドキュメントと格闘しながら、Perl で OAuth を突破して twitter にポストするスクリプトを 0 から書いてみました。

このエントリーをはてなブックマークに追加  

#!/usr/bin/perl

use strict;
use MIME::Base64;
use Digest::HMAC_SHA1;
use HTTP::Request;
use LWP::UserAgent;
use Data::Dumper;

use constant {
    REQUEST_METHOD  =>  'POST',
    REQUEST_URI     =>  'http://api.twitter.com/1/statuses/update.xml',

    CONSUMER_KEY    => 'CONSUMER_KEY',
    CONSUMER_SECRET => 'CONSUMER_SECRET',

    ACCESS_TOKEN    => 'ACCESS_TOKEN',
    ACCESS_SECRET   => 'ACCESS_SECRET',
};

### リクエストパラメータの準備
my %oauth = (
    oauth_consumer_key => CONSUMER_KEY,
    oauth_token => ACCESS_TOKEN,
    oauth_signature_method => 'HMAC-SHA1',
    oauth_timestamp => time,
    oauth_nonce => generate_nonce (),
    oauth_version => '1.0',
);

my %param = (
    status => _encode_url ('Hello, World!'),
);

$oauth{oauth_signature} = get_signature (
    REQUEST_METHOD, 
    REQUEST_URI,
    %oauth,
    %param);

### リクエストを生成する
my $req = HTTP::Request->new (REQUEST_METHOD, REQUEST_URI, [
    'Authorization' => get_oauth_header (%oauth),
], get_content (%param))
    or die 'Failed to initialize HTTP::Request';

### リクエストを投げる
my $ua = LWP::UserAgent->new
    or die 'Failed to initialize LWP::UserAgent';
my $res = $ua->request ($req)
    or die 'Failed to request';
print '-' x 72, "
", "Response
", '-' x 72, "
", Dumper ($res);#DEBUG



### oauth_nonce を生成する
sub generate_nonce {
    my $str = _encode_base64 (pack 'C*', map { int rand 256 } 1..12 );
    # 記号は除く
    $str =~ tr!+=/!012!;
    $str;
}

### 送信するパラメータから oauth_signature を求める
sub get_signature {
    my ($method, $uri, %params) = @_;

    my $params = join '&', map {
        join '=', $_, $params{$_};
    } sort keys %params;

    my $base_string = join '&',
            $method,
            _encode_url ($uri),
            _encode_url ($params);
    my $key = join '&',
            CONSUMER_SECRET,
            ACCESS_SECRET;
    _encode_base64 (Digest::HMAC_SHA1::hmac_sha1 ($base_string, $key));
}

### OAuth HTTP ヘッダ文字列の生成する
sub get_oauth_header {
    my (%param) = @_;
    'OAuth '. join ', ', map {
        sprintf '%s="%s"', $_, _encode_url ($param{$_});
    } keys %param;
}

### POST するデータ文字列を生成する
sub get_content {
    my (%param) = @_;
    join '&', map {
        sprintf '%s=%s', $_, $param{$_};
    } keys %param;
}

### 文字列を URL エンコードする
sub _encode_url {
    my ($str) = @_;
    $str =~ s!([^a-zA-Z0-9_.~-])!sprintf '%%%02X',ord($1)!ge;
    $str;
}

### データを MIME::Base64 エンコードする
sub _encode_base64 {
    my ($str) = @_;
    $str = MIME::Base64::encode ($str);
    # エンコードされた文字列の末尾に改行コードがくっついてくるっぽい
    $str =~ s/^s+|s+$//g;
    $str;
}

メモ

  • OAuth の本来のプロセスでは、サービスプロバイダに問い合わせをして、リクエストトークンの発行をお願いしたりするんだけれど、ボットのように、投稿するアカウントとプログラムが一つしかない場合には、プログラム内部でアクセストークンを固定値で持ってしまってもよいらしい。
  • oauth_nonce にはランダムな文字列が入る。最初、ランダムデータの Base64 エンコードされた文字列を使っていたけれど、記号が入るとシグネチャの生成がうまくいかず認証に失敗したので、記号は除くようにした(generate_nonce)
  • oauth_signature の生成は、説明を読むと結構鬱陶しそうだけれど、スクリプト言語なら割合簡単に書ける(get_signature)
  • エンコードした文字列を二重にエンコードしたりしないように注意する。
  • MIME::Base64::encode では、勝手に末尾に改行を付与してくれるので、自前関数でラッピングした(_encode_base64)
  • ソースコードを UTF-8 で書けば、日本語の tweet もそのまま通る。
  • 上記ソースコードのライセンスは MIT License です。

参考リンク

このエントリーをはてなブックマークに追加  


この記事を読んだ人はこんな記事も読んでいます記事リコメンデーションについて

カバー画像:[Perl] LWP::UserAgent で SSL 証明書のエラーへの対応

関連記事/トラックバック

関連記事/トラックバックはまだありません

この記事にトラックバックを送るには?

寄せられたコメント (全 1 件中、最新 5 件まで表示しています)

Posted by
プリンプリン
at
2011/02/07 07:55
ID
K5lBuT.Q
TwitterAPIを使ったPerlスクリプトを作ろうとしていたのでとても参考になりました。
ありがとうございました。

コメントを投稿する

 
 (必須, 匿名可, 公開, トリップが使えます)
 (必須, 匿名可, 非公開, Gravatar に対応しています)
 (必須)
スパム コメント防止のため「投稿確認」欄に ランダムな数字 CAPTCHAについて を入力してから送信してください。お手数ですがご協力のほど宜しくお願いいたします。