Help us understand the problem. What is going on with this article?

Rails5 APIで認証付きのWebAPIを作ってみる

More than 1 year has passed since last update.

はじめに

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が下記のように記述されます。

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アクションメソッドが出来上がりです。
あとはルート設定とコントローラーの中身をちょこっと作ります。

config/routes.rb
Rails.application.routes.draw do
  resources :users
  resources :memos
  post 'login/login'  # ←これを追記しました
end
app/controllers/login_controller.rb
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の最初と最後に下記のような記述を足します。

app/controllers/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

casareal
システム開発/評価・検証支援/品質改善支援サービスと現場に即した実践的なIT研修サービスを提供しています。
https://www.casareal.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした