Rust Warp: 認証に Cookie を使用


Warp は、Rust の構成可能な Web サーバーです.コードは非常に小さく、速度に重点を置いています.
warp の基本的なビルディング ブロックは Filter であり、それらを組み合わせて構成し、要求に関する豊富な要件を表現できます.

しかし、その概念に慣れていない場合、実際には使いにくいです.また、型システムは初心者を怖がらせることもあります.

たとえば、私は実際に Cookie を認証に使用する方法を理解するのに時間を費やしています.

ユーザーを表す Struct を定義したとします.


#[derive(Debug, Deserialize)]
pub struct User {
    username: String,
    password: String,
}


また、ログイン リクエストに有効なユーザー名とパスワードが含まれているかどうかを承認するロジックがあります.詳細な実装はコードによって異なります.

pub fn verify_user(user: &User) -> bool {
    ....
}


問題は、応答で Cookie を設定する方法と、承認後に各要求を検証する方法です.

クッキーを設定する



ユーザーがログイン リクエストを送信し、認証に合格すると、reply を使用して with_header を使用して、後のリクエストで使用されるトークンを保存するための Cookie を設定します.

 let login = warp::path!("api" / "login")
        .and(warp::post())
        .and(warp::body::json())
        .map(|user: auth::User| {
            if auth::verify_user(&user) {
                let token = auth::gen_token();
                warp::reply::with_header(
                    token.clone(),
                    "set-cookie",
                    format!("token={}; Path=/; HttpOnly; Max-Age=1209600", token),
                )
                .into_response()
            } else {
                warp::reply::with_status("failed", http::StatusCode::UNAUTHORIZED).into_response()
            }
        });
    let routes = routes.or(login);


承認に Cookie を使用する



リクエストを承認するには、Warp で filter を実装する必要があり、このように使用します.ここでは、Filter::untuple_one を使用して、ネストされたタプル レイヤーを抽出から展開します.

  let verify = warp::path!("api" / "verify")
        .and(warp::get())
        .and(auth_validation())
        .untuple_one()
        .map(|| warp::reply::reply().into_response());
    let routes = routes.or(verify);


そして、auth_validation は別の組み込みフィルター warp::cookie を呼び出して、要求ヘッダーから token を抽出します.

struct Unauthorized;

impl reject::Reject for Unauthorized {}

fn auth_validation() -> impl Filter<Extract = ((),), Error = Rejection> + Copy {
    warp::cookie::<String>("token").and_then(|token: String| async move {
        println!("token: {}", token);
        if let Some(true) = auth::verify_token(&token) {
            Ok(())
        } else {
            Err(warp::reject::custom(Unauthorized))
        }
    })
}



いくつかの考え



Rust でコードを書くのに時間を費やしたとしても、Warp などの Web フレームワークの新しい概念や詳細を学ぶ必要があります.私の経験上、他のプログラミング言語に慣れているユーザーにとっては不自然な部分があります.たとえば、フィルタの戻り値の型:

impl Filter<Extract = ((),), Error = Rejection> + Copy


これが何を意味するのかを言うのは簡単ではありませんか?そして、なぜフィルターで untuple_one を呼び出す必要があるのでしょうか?

型システムのルールに従わなければならないこと、Rust コードを書くときにアノテーションを追加する必要があることはわかっていますが、他のプログラミング言語ほど学習が容易ではありません.

それでも、プログラミングにいくつかの新しいものをもたらすので、私はまだ楽しんでいます.