初心者向けガイド:Golang EchoフレームワークでJWT認証を実装する方法(Mac環境)
ウェブアプリケーションの開発において、ユーザー認証は重要な要素の一つです。
特に、セキュアな認証システムを構築するために、JSON Web Tokens(JWT)は広く利用されています。
本記事では、Golang(Go)言語とEchoフレームワークを使用して、JWT認証を実装する方法を初心者にもわかりやすく解説します。
Mac環境での検証を前提としています。
目次
プロジェクトのセットアップ
まず、Golangと必要なパッケージをインストールします。
以下の手順に従ってプロジェクトをセットアップしましょう。
1. Goのインストール
Goがインストールされていない場合は、公式サイトからインストールしてください。
ターミナルで以下のコマンドを実行し、インストールが完了していることを確認します。
go version
出力例:
go version go1.21.4 darwin/amd64
2. プロジェクトディレクトリの作成
ターミナルを開き、以下のコマンドでプロジェクトディレクトリを作成します。
mkdir echo_jwt_auth
cd echo_jwt_auth
3. go.mod
の初期化
Goモジュールを初期化し、必要な依存関係を追加します。
go mod init echo_jwt_auth
go get github.com/labstack/echo/v4
go get github.com/golang-jwt/jwt
go get github.com/joho/godotenv
4. .env
ファイルの作成と秘密鍵の生成
JWTの署名に使用する秘密鍵を生成します。Mac環境では、openssl
コマンドを使用して安全な秘密鍵を生成できます。
ターミナルで以下のコマンドを実行してください。
openssl rand -base64 32
このコマンドは、32バイトのランダムなデータをBase64エンコードした文字列を生成します。生成された文字列をコピーして、後ほど .env
ファイルに貼り付けます。
例:
F2JS6V1YoAM9mxTcvaX0RjLHntQCyq7mTzftufvYmfU=
.env
ファイルをプロジェクトのルートディレクトリに作成し、以下の内容を追加します。
JWT_SECRET=F2JS6V1YoAM9mxTcvaX0RjLHntQCyq7mTzftufvYmfU=
注意:
.env
ファイルには機密情報を含めるため、バージョン管理システム(例:Git)には含めないように.gitignore
に追加することをおすすめします。
主要なファイルの解説
プロジェクトのディレクトリ構造は以下の通りです:
echo_jwt_auth/
├── go.mod
├── server.go
├── go.sum
└── .env
go.mod
Goモジュールの設定ファイルで、プロジェクト名や依存関係を定義します。
module echo_jwt_auth
go 1.21.4
require (
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/labstack/echo/v4 v4.12.0
github.com/joho/godotenv v1.5.1
)
-
module echo_jwt_auth: モジュール名を
echo_jwt_auth
に設定。 - go 1.21.4: 使用するGoのバージョン。
- require: 必要なパッケージを指定。
.env
環境変数を定義するファイルです。ここではJWTの秘密鍵を設定しています。
JWT_SECRET=F2JS6V1YoAM9mxTcvaX0RjLHntQCyq7mTzftufvYmfU=
注意:
.env
ファイルには機密情報を含めるため、バージョン管理システム(例:Git)には含めないように.gitignore
に追加することをおすすめします。
server.go
メインのサーバーコードです。Echoフレームワークを使用してJWT認証を実装します。コードを分割し、メイン関数を簡素化することで、可読性を高めています。
package main
import (
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/golang-jwt/jwt"
"github.com/joho/godotenv"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
// Userはユーザー情報を表す構造体です
type User struct {
Name string `json:"name" xml:"name" form:"name" query:"name"`
Email string `json:"email" xml:"email" form:"email" query:"email"`
Password string `json:"password" xml:"password" form:"password" query:"password"`
}
// JwtCustomClaimsはJWTのカスタムクレームを定義します
type JwtCustomClaims struct {
Email string `json:"email"`
Name string `json:"name"`
jwt.StandardClaims
}
func main() {
// 環境変数の初期化
if err := initEnv(); err != nil {
log.Fatal(err)
}
// Echoインスタンスの作成
e := echo.New()
// ミドルウェアの設定
configureMiddleware(e)
// ルートの定義
defineRoutes(e)
// サーバーの起動
if err := e.Start(":1323"); err != nil {
log.Fatal("サーバーの起動に失敗しました:", err)
}
}
// initEnvは.envファイルから環境変数を読み込みます
func initEnv() error {
err := godotenv.Load()
if err != nil {
return fmt.Errorf(".envファイルの読み込みに失敗しました: %v", err)
}
if os.Getenv("JWT_SECRET") == "" {
return fmt.Errorf("JWT_SECRETが.envファイルに設定されていません")
}
return nil
}
// configureMiddlewareはEchoに必要なミドルウェアを設定します
func configureMiddleware(e *echo.Echo) {
e.Use(middleware.Logger())
e.Use(middleware.Recover())
}
// defineRoutesはアプリケーションのルートとハンドラーを設定します
func defineRoutes(e *echo.Echo) {
// 環境変数からJWTシークレットを取得
jwtSecret := []byte(os.Getenv("JWT_SECRET"))
// JWTミドルウェアの設定
jwtMiddleware := middleware.JWTWithConfig(middleware.JWTConfig{
SigningKey: jwtSecret,
Claims: &JwtCustomClaims{},
})
// パブリックルート: ログイン
e.POST("/login", login)
// 保護されたルート: Hello World
e.GET("/", hello, jwtMiddleware)
}
// helloは保護されたハンドラーで、挨拶メッセージを返します
func hello(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
}
// loginはユーザー認証を行い、JWTトークンを生成して返します
func login(c echo.Context) error {
// リクエストのJSONをUser構造体にバインド
user := new(User)
if err := c.Bind(user); err != nil {
log.Println("リクエストのバインドエラー:", err)
return c.JSON(http.StatusBadRequest, echo.Map{"message": "無効な入力です"})
}
log.Println("ログイン試行:", user.Email)
// 簡易的な認証チェック(実際のアプリケーションではデータベースと連携)
if user.Email != "user@example.com" || user.Password != "password" {
log.Println("認証エラー:", user.Email)
return c.JSON(http.StatusUnauthorized, echo.Map{"message": "メールアドレスまたはパスワードが無効です"})
}
// JWTクレームの設定
claims := &JwtCustomClaims{
Email: user.Email,
Name: user.Name,
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Add(72 * time.Hour).Unix(),
},
}
// クレームを持つトークンを生成
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// トークンを署名
t, err := token.SignedString(jwtSecret)
if err != nil {
log.Println("トークンの署名エラー:", err)
return c.JSON(http.StatusInternalServerError, echo.Map{"message": "トークンを生成できませんでした"})
}
// トークンを返す
return c.JSON(http.StatusOK, echo.Map{
"token": t,
})
}
サーバーの起動とテスト
サーバーの起動
以下のコマンドでサーバーを起動します。
go run server.go
サーバーが正常に起動すると、以下のようなログが表示されます:
2024/04/27 12:00:00 Listening on :1323
テスト手順
APIエンドポイントをテストするために、curl
コマンドを使用します。以下の手順に従ってテストを行ってください。
1. ログインしてJWTトークンを取得
まず、ログインエンドポイントにユーザー情報を送信して、JWTトークンを取得します。
curl -X POST http://localhost:1323/login \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "password", "name": "Sample User"}'
期待されるレスポンス:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJuYW1lIjoiU2FtcGxlIFVzZXIiLCJleHAiOjE3MzA4NTU1MTZ9.1AWyT7129JbdoZ6wx0YGLAXJxY1Y4DxeYxYZ0D75HMI"
}
このトークンを後続のリクエストで使用します。
2. 保護されたエンドポイントにアクセス
取得したJWTトークンを使用して、保護されたルートにアクセスします。
curl http://localhost:1323/ \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJuYW1lIjoiU2FtcGxlIFVzZXIiLCJleHAiOjE3MzA4NTU1MTZ9.1AWyT7129JbdoZ6wx0YGLAXJxY1Y4DxeYxYZ0D75HMI"
期待されるレスポンス:
Hello, World!
3. 認証失敗時の挙動
不正なトークンやトークンを送信しない場合、アクセスは拒否されます。
例: トークンを送信しない場合
curl http://localhost:1323/
期待されるレスポンス:
{
"message": "Missing or malformed JWT"
}
まとめ
本記事では、GolangとEchoフレームワークを使用して、JWT認証を実装する方法を解説しました。
学んだポイント
-
プロジェクトのセットアップ手順:
- Goモジュールの初期化と依存関係の追加方法。
-
JWTの秘密鍵の生成方法(Mac環境):
-
openssl
コマンドを使用した安全な秘密鍵の生成手順。
-
-
Echoフレームワークを用いた基本的なサーバー構築:
- Echoインスタンスの初期化とミドルウェアの設定方法。
-
JWT認証の実装方法:
- ユーザー認証のロジックとJWTトークンの生成・署名方法。
-
curl
コマンドを使用した認証フローのテスト手順:- ログインエンドポイントへのリクエストとトークンを使用した保護されたエンドポイントへのアクセス方法。
参考リンク
これらのリソースを活用して、さらに深い理解と高度な機能の実装に挑戦してみてください!