はじめに
Railsもバージョン5になってから、Web APIを簡単に作れるような仕組みが色々増えました。
認証周りの仕組みもRailsは、手軽にできるようになっているみたいなので試してみます。
使用ソフトウェアのバージョン情報
ruby 2.4.1p111 (2.3でも動作確認しました)
Rails 5.1.3 (5.0.1でも動作確認しました)
記事の中でcurlを使ってますが、RESTクライアントって種類のプラグインがChromeなどではいくつか公開されているので、そちらを使ってみてもいいかもしれません。
準備(アプリケーションの土台を作る)
まずはアプリケーションの土台を作ります。主に作るのは2つのリソース(MemoとUser)です。
Memoが認証で守りたいリソースで、Userはログイン情報とかを持っているリソースという設定です。
下記のコマンドを順番に実行してください。
$ rails new railTokenAuth --api
$ cd railTokenAuth
$ rails g scaffold Memo title:string content:string
$ rails g scaffold User name:string pwd:string token:token
$ rake db:migrate
少し解説すると、ここでUserリソースでtoken型のtokenという名前の属性を与えていますが、これによって
app/models/user.rbが下記のように記述されます。
class User < ApplicationRecord
has_secure_token
end
このhas_secure_tokenという記述を入れることで、Userモデルにデータが追加されたときに、自動的に
Userモデルが持つtokenという属性にセキュアトークンが生成され保存されます。
初期データの投入
まず別のターミナルでrailsサーバーを立ち上げます。以後これはずっと立ち上げっぱなしです。
$ rails server
次にcurlコマンドを使って、POSTメソッドでMemoリソースとUserリソースにデータを投入します。
$ curl -X POST -H 'Content-Type:application/json' -d '{ "name": "testuser", "pwd": "ddddd" }' http://0.0.0.0:3000/users
このコマンドの結果は
{"id":1,"name":"testuser","pwd":"ddddd","token":"rzBHzJi3RuftqmFwhevESoPg","created_at":"2017-08-04T05:07:57.661Z","updated_at":"2017-08-04T05:07:57.661Z"}
token属性になにやら値が入ってますね。
$ curl -X POST -H 'Content-Type:application/json' -d '{ "title": "HELLO!", "content": "NAIYOwaNAIYO" }' http://0.0.0.0:3000/memos
こちらのコマンドの結果はこうなります。
{"id":1,"title":"HELLO!","content":"NAIYOwaNAIYO","created_at":"2017-08-04T05:10:45.410Z","updated_at":"2017-08-04T05:10:45.410Z"}
これでデータの投入が終わりました。
railsコンソールでデータがちゃんと入っているかを確認してみてもいいでしょう。
$ rails console
irb(main):001:0> Memo.all
Memo Load (1.0ms) SELECT "memos".* FROM "memos"
=> #<ActiveRecord::Relation [#<Memo id: 1, title: "HELLO!", content: "NAIYOwaNAIYO", created_at: "2017-08-04 05:10:45", updated_at: "2017-08-04 05:10:45">]>
irb(main):002:0> User.all
User Load (0.2ms) SELECT "users".* FROM "users"
=> #<ActiveRecord::Relation [#<User id: 1, name: "testuser", pwd: "ddddd", token: "rzBHzJi3RuftqmFwhevESoPg", created_at: "2017-08-04 05:07:57", updated_at: "2017-08-04 05:07:57">]>
ちゃんと入っていますね。
ログイン機能を作る
ログイン機能といっても、画面を作るわけではないので、超簡単に作ります。
$ rails g controller login login
これでloginコントローラーのloginアクションメソッドが出来上がりです。
あとはルート設定とコントローラーの中身をちょこっと作ります。
Rails.application.routes.draw do
resources :users
resources :memos
post 'login/login' # ←これを追記しました
end
class LoginController < ApplicationController
def login
# ↓ここから
login_user = User.find_by(name:params[:name],pwd:params[:pwd])
if login_user != nil
render plain: login_user.token
else
render plain: 'no auth'
end
# ↑ここまで追記しました
end
end
すこし迷いましたがログインはPOSTメソッドで行うようにしました。
送られたユーザー名とパスワードに合致するデータがあれば、ログインOK!としてトークンを返しています。
該当するデータがない場合は"no auth"とかってエラーメッセージ(のつもり)の文字列を返しています。
本当はちゃんとステータス(403とか)を返したほうがいいと思います。。。
ちょっとテストしてみます。
$ curl -X POST -H 'Content-Type:application/json' -d '{ "name": "testuser", "pwd": "ddddd" }' http://0.0.0.0:3000/login/login
結果は
rzBHzJi3RuftqmFwhevESoPg
ちゃんとトークンらしい文字列が返ってきています。
ログイン失敗の場合のテストもやっておきましょう。
$ curl -X POST -H 'Content-Type:application/json' -d '{ "name": "testuser", "pwd":"piyopiyo" }' http://0.0.0.0:3000/login/login
no auth
うまく行ってますね。
認証の仕組みを入れる
最後にMemoリソースのコントローラーに認証の仕組みを入れます。
下記のようにmemos_controller.rbの最初と最後に下記のような記述を足します。
class MemosController < ApplicationController
include ActionController::HttpAuthentication::Token::ControllerMethods
before_action :authenticate
# 中略
def authenticate
authenticate_or_request_with_http_token do |token,options|
auth_user = User.find_by(token: token)
auth_user != nil ? true : false
end
end
end
試してみましょう。
$ curl -X GET -H 'Authorization: Token rzBHzJi3RuftqmFwhevESoPg' -H 'Content-Type:application/json' http://0.0.0.0:3000/memos/1
'Authorization: Token rzBHzJi3RuftqmFwhevESoPg' の中のトークンはさっきログインした時にもらったトークンをそのまま貼り付けてください。
結果は次のようになります。ちゃんとデータが返って来ていますね。
{
"id": 1,
"title": "HELLO!",
"content": "NAIYOwaNAIYO",
"created_at": "2017-08-04T05:10:45.410Z",
"updated_at": "2017-08-04T05:10:45.410Z"
}
だめな場合も見ておきます。
$ curl -X GET -H 'Authorization: Token piyopiyo' -H 'Content-Type:application/json' http://0.0.0.0:3000/memos/1
HTTP Token: Access denied.
ちゃんと拒否されていますね。
これで完成です。
あと残る問題はこのトークンをどうやってクライアントで保持させるかですね。
Web Storageで持っておくものなのかな? (どなたか教えてくださると嬉しいです)
Node.jsで作ってみる
@oz4youさんが、Node.jsで動作するテスト用の認証付きWebAPI(JSON Server)を作成してくれました。
http://qiita.com/oz4you/items/3f3223048bd2109de47b