カタカタブログ

SIerで働くITエンジニアがカタカタした記録を残す技術ブログ。Java, Oracle Database, Linuxが中心です。たまに数学やデータ分析なども。

ZaimのAPIをRuby on RailsからOAuthを使って叩いてみる

普段は会計簿をつけているZaimというお気に入りのWebサービスがあるのだが、標準で用意されている分析がいまいち細かいところまで見れないので、自分でデータを取得してみることにした。
https://zaim.net

Zaimは開発者向けのAPIが提供されているようだが、OAuth 1.0aで認証されているので、Ruby on RailsのアプリケーションからZaimにOAuthで認証し、Zaimの家計簿データを取得して画面表示するところまでをやってみた。

今回の環境は以下。

  • Mac OS X Yosemite 10.10.5
  • Ruby 2.2.2
  • Rails 4.2.3

Zaim APIからアプリケーションを登録する

まず、Zaim開発センターというページにて、アクセスするアプリケーションを登録する。

以下にログインし、「新しいアプリケーションを追加」をクリックする。
https://dev.zaim.net/

今回はRailsによるWebアプリなので、サービス種は「ブラウザアプリ」としておく。
また、サービスのURLは今回はlocalhostで起動するWebアプリを想定して、localhostのURLを指定しておく。
ちなみにポート番号は指定出来ないもよう(フォーマットエラーになる)。
※アプリケーション名は適当に変更すること
f:id:osn_th:20151102163618p:plain
以下のような画面が出るので、Consumer KeyとConsumer Secretをメモしておく。これはOAuthでアクセスするときにアプリケーションを特定するために使う。
f:id:osn_th:20151102163635p:plain

Ruby on RailsからZaim連携アプリケーション開発

今回はRuby on Railsの画面から、OAuthを使って以下の流れでZaimデータを取得する画面を作ってみる。

  1. アプリケーションにアクセスし、Zaim連携ボタンをクリックする
  2. Zaimに転送され、連携を許可する
  3. Zaimのデータを取得する

まず、RailsでOAuthを使うための’oauth’というgemをgemfileに追加し、bundle install

gem 'oauth'

続いて、Railsでビューとコントローラを作成する。
今回はDBにデータを保持しないので、モデルは作らない。

$  rails g controller zaimauth

コントローラを実装する。中身の詳細は後ほど。
(CONSUMER_KEYやCONSUMER_SECRETは環境変数に切り出すべきだが。。今回は分かりやすさのためクラス定数にしてしまう)。

require "json"
require "oauth"
class ZaimauthController < ApplicationController
  CONSUMER_KEY = '<メモしたConsumer Key>'
  CONSUMER_SECRET = '<メモしたConsumer Secret>'
  CALLBACK_URL = 'http://localhost/callback'
  API_URL = 'https://api.zaim.net/v2/'

  def top
  end

  def login
    set_consumer
    @request_token = @consumer.get_request_token(oauth_callbackCALLBACK_URL)
    session[:request_token] = @request_token.token
    session[:request_secret] = @request_token.secret
    redirect_to @request_token.authorize_url(:oauth_callback => CALLBACK_URL)
  end

  def callback
    if session[:request_token] && params[:oauth_verifier]
      set_consumer
      @oauth_verifier = params[:oauth_verifier]
      @request_token = OAuth::RequestToken.new(@consumer, session[:request_token], session[:request_secret])
      access_token = @request_token.get_access_token(:oauth_verifier => @oauth_verifier)
      session[:access_token] = access_token.token
      session[:access_secret] = access_token.secret
      redirect_to money_path
    else
      logout
    end
  end

  def money
    set_consumer
    @access_token = OAuth::AccessToken.new(@consumer, session[:access_token], session[:access_secret])
    money = @access_token.get("#{API_URL}home/money")
    @money = JSON.parse(money.body)
  end

  def logout
    session[:request_token] = nil
    session[:access_token] = nil
    redirect_to '/zaimauth'
  end

  private

  def set_consumer
    @consumer = OAuth::Consumer.new(CONSUMER_KEYCONSUMER_SECRET,
      site: 'https://api.zaim.net',
      request_token_path: '/v2/auth/request',
      authorize_url: 'https://auth.zaim.net/users/auth',
      access_token_path: '/v2/auth/access')
  end
end

ビューも簡単に実装してしまう。

  • top.html.haml
%p= link_to 'login', login_path
%p= link_to :logout
  • money.html.haml
%p= @money

ルーティングも忘れずに。

  • routes.rb
Rails.application.routes.draw do
  get 'top' => 'zaimauth#top'
  get 'callback' => 'zaimauth#callback'
  get 'login' => 'zaimauth#login'
  get 'money' => 'zaimauth#money'
end

動作確認

さて、これで一通り動くものができたはず。画面を動かしながら、上で書いたコードを見る。

railsサーバ起動

Zaim APIにて、サービスのURLをポート番号なしでで設定しているため、
デフォルトの3000番ポートではなく、80番ポートで起動した。
普通にやるとエラーになったため、sudoをつけると回避できた。

$ sudo rails s -p 80

アプリケーションにアクセス

まずトップページを表示する。
http://localhost/top

topページは何もロジックはなく、loginリンクをクリックすると、http://localhost/login へ遷移する。
f:id:osn_th:20151102163632p:plain
http://localhost/login
コントローラのloginメソッドは以下のような実装をしている。

  def login
    set_consumer
    @request_token = @consumer.get_request_token(oauth_callbackCALLBACK_URL)
    session[:request_token] = @request_token.token
    session[:request_secret] = @request_token.secret
    redirect_to @request_token.authorize_url(:oauth_callback => CALLBACK_URL)
  end

set_consumerでコンシューマオブジェクトを初期化している。request_tokenは後から使う必要があるため、tokenとsecretの値をsessionに保持しておく。
redirect_to @request_token.authorize_url(:oauth_callback => CALLBACK_URL)とすることで、ZaimのOAuth認証画面へリダイレクトされる。このときにoauth_callbackにアプリケーションの戻りのパスを渡しておくと、Zaimで認証が通った後に、自動的にリダイレクトしていくれる。

loginリンクをクリックすると、以下のようにZaimにとばされるので、ログインする。
f:id:osn_th:20151102163626p:plain
正常にログインできると、認証が完了のメッセージが表示され、少し待つとリダイレクトされる。
f:id:osn_th:20151102163630p:plain

リダイレクト先のURLは以下を指定しておいたが、ZaimでOAuthの認証が成功すると、自動でこのURLにリダイレクトしてくれる。さらにこのとき、URLパラメータでoauth_verifierの値がいっしょに渡される。これはOAuthの認証を通過したユーザが、APIのアクセストークンを取得するときに必要となる文字列のため、callback先でsessionに保存しておく。
http://localhost/callback

def callback
    if session[:request_token] && params[:oauth_verifier]
      set_consumer
      @oauth_verifier = params[:oauth_verifier]
      @request_token = OAuth::RequestToken.new(@consumer, session[:request_token], session[:request_secret])
      access_token = @request_token.get_access_token(:oauth_verifier => @oauth_verifier)
      session[:access_token] = access_token.token
      session[:access_secret] = access_token.secret
      redirect_to money_path
    else
      logout
    end

  end

@oauth_verifier = params[:oauth_verifier]はURLパラメータで渡されたoauth_verifierの値を切り出している。
その値を使ってrequest_tokenを取得し、そこからget_access_tokenでアクセストークンを取得している
アクセストークンの取得に成功すると、access_tokenとaccess_token_secretという二つの文字列が手に入るが、この二つを使うと以降APIにアクセスできる。そのため、この二つをsessionに保存しておく。

あとは通常の画面だとリクエストをする画面にでも飛ばせばよいが、今回はZaimの全データをこのまま取得してみる。
callbackからmoney_pathにリダイレクトしているが、moneyでは以下のロジックを実装している。

def money
    set_consumer
    @access_token = OAuth::AccessToken.new(@consumer, session[:access_token], session[:access_secret])
    money = @access_token.get("#{API_URL}home/money")
    @money = JSON.parse(money.body)

  end

保存しておいたaccess_tokenとaccess_token_secretを使ってアクセストークンオブジェクトを再構築し、getメソッドからZaimのAPIにアクセスしている。

なお、ZaimのAPIの仕様は以下にまとまっている。
https://dev.zaim.net/home/api

今回、ビュー側では取得JSON形式のAPI問合せ結果をそのままま画面に表示している。
f:id:osn_th:20151102163623p:plain

これで、Zaimに問合せたデータが正しく画面にまで受け取れていることが分かる!

まとめ

OAuthの仕組みを理解してしまえば、比較的簡単にZaimのデータを取得できることが分かった。Zaim API自体の情報はそれほど多くはなかったが、twitter APIなどにOAuthでアクセスするのとほとんど変わらないので、OAuth自体を勉強するいい機会になった。
あとはとったデータを好きに料理すればよい!また、データ登録用のAPIもあるので、自分のZaimクライアントを作ることもできそう!

以上