Contents
Actix-Webプロジェクトの初期設定と依存関係導入
RustプロジェクトでActix-Webを使用する際は、まず基本的な構成を整える必要があります。非同期処理を活かした設定も同時に確認しましょう。
必須依存関係とプロジェクト構造の説明
Actix-Web本体とJWTライブラリの導入は以下のコードで行います。jsonwebtokenクレートはトークン生成・検証に必須です。公式ドキュメントはこちら: https://github.com/Keats/jsonwebtoken。
|
1 2 3 4 5 6 |
[dependencies] actix-web = "4.0" jsonwebtoken = "8.0" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } |
プロジェクト構成の例
新規作成したプロジェクトの構造は以下の通りです。src/main.rsにルート定義、src/auth.rsなどに認証ロジックを分離することで保守性を高められます。
|
1 2 3 4 5 6 |
my_actix_app/ ├── Cargo.toml └── src ├── main.rs └── auth.rs |
JWT署名・検証ロジックの実装
JWTの有効期限管理やエラーハンドリングは、セキュリティにおいて不可欠です。HS256アルゴリズムによる署名処理を具体例に解説します。
セクレットキーの管理方法と注意点
環境変数で管理するのがセキュアな方法です。env!マクロで取得するか、dotenvクレートを使用すると便利です。
|
1 2 3 4 |
use std::env; let secret = env::var("JWT_SECRET").expect("JWT_SECRET must be set"); |
注意:
dotenvクレートを導入する場合はCargo.tomlに以下の記述が必要です。
toml
[dependencies]
dotenv = "0.15"
トークン生成と有効期限設定の手順
以下のコードは1時間の有効期間を持つトークンを生成する例です。jsonwebtoken::encodeを使用し、Headerにアルゴリズム(HS256)を指定します。
|
1 2 3 4 5 6 7 8 9 |
use jsonwebtoken::{EncodingKey, Header}; let claims = Claims { sub: user_id.to_string(), exp: (Utc::now() + Duration::hours(1)).timestamp() as usize, }; let token = jsonwebtoken::encode(&Header::default(), &claims, &EncodingKey::from_secret(secret.as_ref())) .expect("Token creation failed"); |
検証時のエラーハンドリングと処理方法
トークンの検証では、decodeメソッドがJsonWebTokenErrorを返すため、適切な処理が必要です。
|
1 2 3 4 5 6 7 8 |
match jsonwebtoken::decode(&token, &DecodingKey::from_secret(secret.as_ref()), &Validation::default()) { Ok(validated_token) => { /* 正常に認証 */ }, Err(e) => match e { JsonWebTokenError::InvalidSignature => { /* 署名不一致 */ } _ => { /* その他のエラー処理 */ } } } |
認証ミドルウェアの設計と実装
Actix-WebのMiddleware traitを実装することで、トークン検証を全リクエストに自動適用できます。非同期処理によるパフォーマンス改善も可能です。
ヘッダーからのトークン取得ロジック
リクエストヘッダーからAuthorizationフィールドを取得し、Bearerトークンの形式で解析します。
|
1 2 3 4 5 6 7 |
fn extract_token(req: &HttpRequest) -> Option<String> { req.headers() .get("Authorization") .and_then(|header| header.to_str().ok()) .and_then(|s| s.strip_prefix("Bearer ").map(String::from)) } |
非同期処理の活用とパフォーマンス改善
async_traitを導入し、トークン検証を非同期に実行することで、I/O待ち時間を他のリクエストに譲渡できます。公式ドキュメントはこちら: https://github.com/danburkland/async-trait。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
use async_trait::async_trait; #[derive(Clone)] struct AuthMiddleware { secret: String, } #[async_trait] impl Middleware for AuthMiddleware { async fn handle(&self, mut req: HttpRequest, next: Next) -> Result<HttpResponse> { let token = extract_token(&req); if let Some(token) = token { match validate_token(&token, &self.secret).await { Ok(user_id) => { req.extensions_mut().insert(user_id); next.call(req).await } Err(e) => Err(HttpResponse::Unauthorized().body(format!("認証失敗: {}", e))), } } else { Err(HttpResponse::Unauthorized().body("トークンが見つかりません")) } } } |
セキュリティベストプラクティスの導入
セキュリティについては、以下の要素に注目して設計することが重要です。
ブラックリストと有効期限管理の比較
JWTは無状態性のため、一旦発行されたトークンを即座に無効化できません。代替としてブラックリスト(Redisなど)への登録や有効期限の短縮が有効です。
|
1 2 3 4 5 |
| 対策 | 方法 | 補足 | |------|------|------| | 有効期限短縮 | 指定時間(例:15分)に設定 | クライアント側でリフレッシュトークンを用いる仕組みが必要 | | ブラックリスト | Redisなどにトークンを保存 | 非同期検証処理と併用推奨 | |
秘密鍵の環境変数管理
dotenvクレートを使用して.envファイルから読み込む方法が安全です。
|
1 2 3 |
[dependencies] dotenv = "0.15" |
|
1 2 3 4 5 6 7 8 |
use dotenv::dotenv; use std::env; fn main() { dotenv().ok(); let secret = env::var("JWT_SECRET").expect("JWT_SECRET must be set"); } |
エンドポイントごとのアクセス制御実装
Actix-WebのGuard機能と組み合わせて、認証状態に応じた動的なアクセス制御が可能です。
認証が必要なエンドポイントの指定方法
actix-web::guard::fn_guardでカスタムガードを定義し、ミドルウェアやルートに適用します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
use actix_web::{web, guard}; fn auth_required(req: &HttpRequest) -> bool { req.extensions().get::<String>().is_some() } let app = App::new() .service( web::resource("/api/protected") .guard(guard::fn_guard(|req| Box::pin(async move { auth_required(req) }))) .to(|| HttpResponse::Ok().body("認証済み")), ); |
ユーザー権限レベルの検証ロジック
ペイロードにroleフィールドを追加し、アクセス制御時に検証します。
|
1 2 3 4 5 6 7 |
#[derive(Serialize, Deserialize)] struct Claims { sub: String, role: String, exp: usize, } |
JWT認証フローの実装サンプル
ログインエンドポイントの実装
ユーザー情報を検証し、成功時にJWTトークンを発行する例です。
|
1 2 3 4 5 6 7 8 9 10 |
#[post("/login")] async fn login( user: web::Json<LoginRequest>, pool: web::Data<PgPool>, ) -> Result<HttpResponse> { // ユーザー検証ロジック... let token = generate_token(user.id); Ok(HttpResponse::Ok().json(LoginResponse { token })) } |
保護されたリソースへのアクセステスト
認証ミドルウェアを適用したエンドポイントにAuthorization: Bearer <トークン>を付与し、アクセス確認します。
|
1 2 |
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." http://localhost:8080/api/protected |
非同期処理により、リクエストの並列処理が可能となり、負荷に強いAPI構築が実現できます。