CodeIgniterとJSON Web Tokenを使用したセキュアなRESTful APIの構築方法
読む所要時間: 24 分
クラウドサービスの利用と応用が拡大する中で、Simple Object Access Protocol(SOAP)よりも効率的なアーキテクチャスタイルの必要性が増しています。REST(REpresentational State Transfer)はクライアントとApplication Programming Interface(API)との軽量でステートレスな通信を可能にします。RESTを使った通信はステートレスです。Restful APIのアクセスコントロールはトークンベースで行われ、クライアントが要求したアクションをリソースに実行する権限があるかどうかを判別する情報が伝えられます。
本稿では、CodeIgniterを使用したRESTful APIの作成方法を紹介します。CodeIgniterは強力な機能を持つPHPフレームワークで、非常に小さなリソースでフルスケールのWebアプリケーションを構築できます。
必要条件
CodeIgniterの基本的な知識があると役立ちます。CodeIgniterの知識に不安がある場合も、チュートリアルの中で説明とともに公式ドキュメントへのリンクを紹介します。不明な点があれば、リンクを参照しながらチュートリアルを進めてください。
また、以下の項目がインストールされている必要があります。
- Composer
CodeIgniterプロジェクトの依存関係管理に使用します。 - ローカルデータベースインスタンス
このチュートリアルではMySQLを使用しますが、お好きなデータベースサービスを選んで構いません。 - Postman(または同等のアプリケーション)
エンドポイントのテストに使用します。エンドポイントのテストにはcURLも使用できます。
作成するAPI
企業向けの顧客データベースを管理するAPIを構築し、セキュアなCodeIgniter APIの構築方法を学びます。顧客データベースにはそれぞれ次のデータが含まれます。
- 名前
- メール
- サービスの維持に必要な料金(維持費)
作成するAPIは次の機能を持ちます。
- 新規ユーザーの登録
- 既存ユーザーの認証
- 新規顧客の追加
- 既存顧客情報の編集
- 全顧客の表示
- IDを指定した顧客表示
- IDを指定した顧客削除
3〜7の機能を実行できるのは認証されたユーザーのみです。
プロジェクトの準備
Composerを使用して新規CodeIgniterプロジェクトを作成します。以下のコマンドを実行します。
ci-secure-apiディレクトリに新しいCodeIgniterプロジェクトが作成されます。インストールが完了したら、ターミナルで新しく作成したプロジェクトのディレクトリに移動し、CodeIgniterでインストールされたローカルの開発サーバーでアプリケーションを実行します。次のコマンドを実行します。
ブラウザからhttp://localhost:8080/にアクセスすると、スタートページが表示されます。
環境変数の設定
CodeIgniterのインストールと実行が完了しました。次にアプリケーションで使用する環境変数を設定します。CTRL + Cでアプリケーションの実行を停止し、次のコマンドでenvファイルのコピーを.envと命名して作成します。
CodeIgniterはデフォルトで、本番環境モードで起動しますが、本稿では開発モードに変更します。次のコメントを解除して開発モードに設定します。
ローカル環境にデータベースを作成し、次の変数のコメントを解除して値を更新し、データベースに正しく接続できるように設定します。
YOUR_DATABASE
、YOUR_DATABASE_USERNAME
、YOUR_DATABASE_PASSWORD
を実際の値に置き換えます。
マイグレーションとシーダー
これでデータベースが作成され、接続が設定されました。次に、user
テーブルとclient
テーブルのマイグレーションを作成します。マイグレーションファイルは一般的に、適切なデータベース構造を作成するのに有効です。マイグレーションファイルとシーダーの作成には、CodeIgniter CLIツールを使用します。
ターミナルで次のコマンドを実行します。
CLIでマイグレーションファイルの名前を指定すると、App/Database/Migrationsディレクトリにマイグレーションファイルが作成されます。次の2つのマイグレーションファイルを作成します。
add_client
add_user
マイグレーションファイル名の先頭にYYYY-MM-DD-HHIISS
形式の日付が付きます。詳しい説明はCodeIgniterドキュメントを参照してください。
次に、add_client
マイグレーションファイルの内容を次のように変更します。
client
テーブルのフィールドと対応するデータタイプを指定しています。
次に、add_user
マイグレーションファイルを開き、次のように内容を変更します。
このコードで、ユーザーテーブルとフィールドが作成されます。次のコマンドを使用してマイグレーションを実行します。
開発を進めやすいように、データベースにダミーの顧客データを含めておきます。CodeIgniterスケルトンにデフォルトで含まれる依存パッケージであるfzaninotto faker bundleを使用し、データベースにランダムに顧客を追加します。マイグレーションと同じく、CodeIgniterのCLIツールを使用して顧客のシーダーを作成します。次のコマンドを実行します。
CLIで名前にClientSeeder
を指定します。ClientSeeder.phpファイルがApp/Database/Seedsディレクトリに作成されます。このファイルを開き、以下の内容に置き換えます。
次のコマンドを使用し、データベースにダミー顧客を配置します。
この時点でデータベースの構造は次の画面のようになります。
エンティティモデル
APIによるデータベース操作にはCodeIgniterモデルを使用します。これを機能させるために2つのモデルを作成します。1つはUser用、もう1つはClient用です。
App/Modelsディレクトリを開き、次のファイルを作成します。
- UserModel.php
- ClientModel.php
UserModel.phpに、次の内容を追加します。
beforeInsert
関数とbeforeUpdate
関数により、データベースに保存する前にUserエンティティに対する処理を実行できます。このコードでは、データベースに保存する前のユーザーパスワードをハッシュ化しています。
ClientModel.phpファイルに次のコードを追加します。
$table
フィールドは、どのデータベーステーブルを主として扱うかをモデルに指示します。$allowedFields
はテーブルの更新可能な列を指示します。findClientById
関数が抽象化を行い、指定のid
をもとにデータベースから顧客を取得します。
モデルとデータベースの実装が完了しました。これでユーザーを追加して認証が行えます。認証されたユーザーは現行の顧客の操作も行えます。
JSON Web Tokenの実装
JSON Web Token(JWT)を使用してユーザーを認証し、無許可のユーザーによる顧客リストの閲覧を禁止します。このために、ユーザーの登録時や正常にログインしたときに、APIがトークンを発行します。このトークンが以後のリクエストのヘッダーに追加され、APIはリクエスト元のユーザーを正しく認識することができます。このチュートリアルでは、firebase/php-jwtバンドルを使用してトークンを生成します。
次のコマンドを実行し、Composerを使用してインストールします。
インストールが完了したら、次の内容を.envファイルに追加します。
次に、Servicesクラスの秘密キーを取得するヘルパー関数を作成します。App/Config/Services.phpを開いて次の内容を追加します。
JWTヘルパーの作成
トークンの生成と検証のために、ヘルパーファイルを作成します。これによりアプリケーションをモジュール化できます。App/Helpersディレクトリにjwt_helper.phpを作成します。ヘルパーファイルの内容は次のように設定します。
getJWTFromRequest
関数が受信リクエストの認証ヘッダーをチェックし、トークン値を返します。ヘッダーが欠落している場合は、例外として処理され、HTTP_UNAUTHORIZED
(401)が返されます。
validateJWTFromRequest
関数は、getJWTFromRequest
関数により取得したトークンをデコードし、生成されたキーに対応するメールアドレスを取得します。そして、そのメールアドレスのユーザーをデータベースから検索します。ユーザーが見つからなければ、Userモデルが例外として処理し、ユーザーにHTTP_UNAUTHORIZED
(401)が返されます。
getSignedJWTForUser
関数を使用し、認証されたユーザーにトークンを生成します。エンコード済みのJWTには次の情報が含まれます。
- 認証済みユーザーのメールアドレス。これを以後のリクエストに使用し、リクエスト元を検証します。
- トークンが生成された時間(
iat
)。 - トークンの有効期限が終わる時間(
exp
)。これを取得するために、.envファイルからJWT_TIME_TO_LIVE
値を現在時刻に追加します。
認証フィルターの作成
App/FiltersディレクトリにJWTAuthenticationFilter.phpを作成します。このフィルターにより、APIはコントローラーにリクエストを渡す前にJWTをチェックします。JWTが提供されない場合や、JWTが有効期限切れの場合は、APIによりHTTP_UNAUTHORIZED
(401)レスポンスとエラーメッセージが返されます。次の内容をファイルに追加します。
このように、まずJWTヘルパーが読み込まれてから、getJWTFromRequest
とvalidateJWTFromRequest
関数を使用してリクエスト元が有効なトークンを持つ認証済みユーザーであることを確認します。
JWTAuthenticationフィルターを登録し、保護するルートを指定します。これはApp/Config/Filters.phpファイルで行います。$aliases
と$filters
の配列を次のように変更します。
以上を追加すると、clientで始まるエンドポイント宛にリクエストが送られるたびに、JWTAuthenticationFilter.phpのbefore
関数が呼び出されます。これにより、リクエストヘッダーに有効なトークンが含まれる場合のみ、コントローラーはリクエストを受信して処理します。
ここではコントローラーを使用していませんが、これまでに作成したアプリケーションの動作を確認することができます。Postmanを開き、http://localhost:8080/client宛にGETリクエストを実行します。次のような結果が確認できます。
そして、App/Controllers/BaseController.phpファイルを開き、次の関数を追加します。
コントローラーはこの関数を使用し、クライアントにJSONレスポンスを返します。
BaseController
によりCodeIgniterのController
が拡張され、受信リクエストの処理を支援するヘルパーとその他の機能が提供されます。その1つであるvalidate
関数は、CodeIgniterの検証サービスを使用し、コントローラー関数に指定されたルール(必要があればエラーメッセージ)をもとにリクエストをチェックします。この関数はフォームリクエスト(Postmanを使用したフォームデータ)は適切に処理しますが、APIに送られた未処理のJSONリクエストの検証はできません。これは、フォームデータリクエストの中身がリクエストのpost
フィールドに格納されるのに対し、JSONリクエストの中身がリクエストのbody
フィールドに格納されるためです。
この問題を避けるため、リクエストの両方のフィールドをチェックする関数を作成して中身を取得します。App/Controllers/BaseController.phpに以下を追加します。
次に、前述した関数から取得した$input
に検証サービスを実行する関数を宣言します。この関数はビルトインされているvalidate
関数とほぼ同じですが、IncomingRequest
にチェックを実行する代わりに、getRequestInput
関数から取得した入力にチェックを実行します。
以上の準備ができたら、ユーザーを登録して認証を行うロジックを追加します。
認証コントローラー
次に、App/ControllersディレクトリにAuth.phpを作成します。次のようにファイルを変更します。
登録
新しいユーザーを正しく登録するために、次のフィールドが必要です。
- 名前
- メールアドレス
8文字以上、255文字以内の有効な形式である必要があります。 - パスワード
8文字以上、255文字以内。
受信したリクエストは、指定されたルールに従いチェックされます。無効なリクエストは破棄され、HTTP_BAD_REQUEST
コード(400)とエラーメッセージが返されます。リクエストが有効であれば、ユーザーデータが保存され、保存されたデータとともに(パスワードは除く)トークンが返されます。HTTP_CREATED
(201)レスポンスによりクライアントに新しいリソースが作成されたことが通知されます。
登録エンドポイント(http://localhost:8080/auth/register)に有効なname
、email address
とpassword
によるPOSTリクエストを実行すると、次のようなレスポンスが返されます。
認証
正しく認証を行うために、以下の項目が必要になります。
- メールアドレス
8文字以上、255文字以内の有効な形式である必要があります。メールアドレスは、保存されたユーザーのものに一致しなければなりません。 - パスワード
8文字以上、255文字以内である必要があります。メールアドレスと同じく、送られるパスワードのハッシュは、メールアドレスに関連して保存されたパスワードのハッシュと一致しなければなりません。
しかし、同じ操作をログインエンドポイント(http://localhost:8080/auth/login)に行うと、Internal Server Error(HTTP Code 500)が発生します。これは、検証ルールに使用するvalidateUser
関数がまだ作成されていないためです。
ユーザーの検証
appディレクトリに新しいディレクトリValidationを作成します。このapp/ValidationディレクトリにUserRules.phpを作成し、次のコードを追加します。
次に、App/Config/Validation.phpファイルを開いて$ruleSets
の配列を変更し、使用するUserRulesを追加します。$ruleSets
は次のように設定します。
カスタム検証ルールが追加され、認証リクエストが正しく機能するようになります。テストするために、POST HTTPリクエストをhttp://localhost:8080/auth/loginエンドポイントに送信します。先ほど作成したユーザーのデータを使用します。
顧客コントローラーの作成
顧客コントローラーでは、app/Config/Routes.phpファイルにルートを指定します。ファイルを開き、次のルートを追加します。
これにより、APIは同じエンドポイントで別のHTTP verbを使用してリクエストを適宜処理できます。
次に、App/ControllersディレクトリにClient.phpを作成します。ファイル内容は次のように設定します。
index
、store
、show
関数を使用し、それぞれ全顧客の表示、新規顧客の追加、単一顧客の表示のリクエストを処理します。
続いて、以下のようにupdate
とdestroy
の2つの関数を作成します。update
関数は顧客の編集のリクエスト処理に使用します。必須のフィールドはありません。予期される値がリクエストに含まれない場合は、値を削除してからデータベースの顧客が更新されます。destroy
関数は特定の顧客を削除するリクエストを処理します。
以上ができたら、APIの準備は完了です。アプリケーションを以下のコマンドで再起動します。
リクエストを送信してテストします。Postman、cURL、あるいはお好きなアプリケーションを使用してテストできますが、本稿ではPostmanを使用します。
アクセストークンの追加
レスポンスのaccess_token
の値をコピーします。[Authorization]
タブをクリックし、ドロップダウンから[Bearer Token]
を選択し、コピーしたaccess_token
の値を貼り付けます。
新規顧客の作成
新しい顧客を作成するために、POST
HTTPリクエストをhttp://localhost:8080/clientに送ります。
全顧客の表示
作成された顧客のリストを取得するために、GET
HTTPリクエストをhttp://localhost:8080/clientに送ります。
IDを指定した顧客情報の取得
特定の顧客の情報を取得するために、GET
HTTPリクエストをhttp://localhost:8080/client/1に送ります。ここでは1
を使用し、データベースから取得する顧客の一意のid
を指定しています。
まとめ
本稿では、CodeIgniterを使用してPHPベースのAPIを作成しました。このAPIを使って、リソース(顧客)に基本的なCRUD(Create、Read、Update、Delete)操作を実行できます。また、リソースへのアクセスを制限するセキュリティレイヤーも追加しました。アプリケーションをモジュール化して疎結合にするプロジェクト構成の方法も学びました。
チュートリアルで使用したすべてのコードベースはGitHubで確認できます。ぜひご活用ください。一緒にコーディングを楽しみましょう!
Oluyemi氏は、電気通信工学のバックグラウンドを持つ技術愛好家です。ユーザーが直面する日々の問題を解決することに強い関心を持ち、プログラミングの道に進んで以来、Webとモバイルの両方のソフトウェア開発で問題解決能力を磨いてきました。Oluyemi氏は、知識の共有に情熱を注ぐフルスタックのソフトウェアエンジニアであり、いくつかのブログで多数の技術記事とコンテンツをインターネットに公開しています。技術にも精通しており、趣味は新しいプログラミング言語とフレームワークを試すことです。
- Twitter: https://twitter.com/yemiwebby
- GitHub: https://github.com/yemiwebby
- Webサイト: https://yemiwebby.com.ng/