曲速(Rust)中的API密钥验证



我正在尝试开始使用warp并测试api密钥验证。下面的代码可以工作,但不太好。

验证函数从标头中提取密钥。在成功验证之后,不再使用密钥;handle_request";函数需要有一个输入参数。

你能建议如何避免不需要的输入参数和使用warp进行api密钥验证的更干净的方法吗?

提前感谢!

use std::convert::Infallible;
use std::error::Error;
use serde::{Deserialize, Serialize};
use warp::http::{Response, StatusCode};
use warp::{reject, Filter, Rejection, Reply};
//use futures::future;
// use headers::{Header, HeaderMapExt};
// use http::header::HeaderValue;
// use http::HeaderMap;
extern crate pretty_env_logger;
#[macro_use] extern crate log;
#[derive(Deserialize, Serialize)]
struct Params {
key1: String,
key2: u32,
}
#[derive(Debug)]
struct Unauthorized;
impl reject::Reject for Unauthorized {}
#[tokio::main]
async fn main() {
pretty_env_logger::init();
// get /exampel?key1=value&key2=42
let route1 = warp::get().and(key_validation())
.and(warp::query::<Params>())
.and_then(handle_request);

let routes = route1.recover(handle_rejection);
warp::serve(routes)
.run(([127, 0, 0, 1], 3030))
.await;
}
async fn handle_request(api_key:String, params: Params) -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder().body(format!("key1 = {}, key2 = {}", params.key1, params.key2)))
}
fn key_validation() -> impl Filter<Extract = (String,), Error = Rejection> + Copy {
warp::header::<String>("x-api-key").and_then(|n: String| async move {
if n == "test" {
Ok(n)
} else {
Err(reject::custom(Unauthorized))
}
})
}
// JSON replies
/// An API error serializable to JSON.
#[derive(Serialize)]
struct ErrorMessage {
code: u16,
message: String,
}
// This function receives a `Rejection` and tries to return a custom
// value, otherwise simply passes the rejection along.
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
let code;
let message;
if err.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "NOT_FOUND";
} else if let Some(Unauthorized) = err.find() {
code = StatusCode::UNAUTHORIZED;
message = "Invalide API key";
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
// We can handle a specific error, here METHOD_NOT_ALLOWED,
// and render it however we want
code = StatusCode::METHOD_NOT_ALLOWED;
message = "METHOD_NOT_ALLOWED";
} else {
// We should have expected this... Just log and say its a 500
error!("unhandled rejection: {:?}", err);
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "UNHANDLED_REJECTION";
}
let json = warp::reply::json(&ErrorMessage {
code: code.as_u16(),
message: message.into(),
});
Ok(warp::reply::with_status(json, code))
}

更新:当我试图避免用";key_ validation";函数我得到这个错误:

error[E0271]: type mismatch resolving `<warp::filter::and_then::AndThen<impl warp::Filter+Copy, [closure@src/main.rs:44:50: 50:6]> as warp::filter::FilterBase>::Extract == ()`
--> src/main.rs:43:24
|
43 | fn key_validation() -> impl Filter<Extract = (), Error = Rejection> + Copy {
|                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected tuple, found `()`
|
= note:  expected tuple `(_,)`
found unit type `()`

为了解决这个问题,我尝试了:

async fn handle_request(params: Params) -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder().body(format!("key1 = {}, key2 = {}", params.key1, params.key2)))
}
fn key_validation() -> impl Filter<Extract = ((),), Error = Rejection> + Copy {
warp::header::<String>("x-api-key").and_then(|n: String| async move {
if n == "test" {
Ok(())
} else {
Err(reject::custom(Unauthorized))
}
})
}

结果是:

error[E0593]: function is expected to take 2 arguments, but it takes 1 argument
--> src/main.rs:31:19
|
31 |         .and_then(handle_request);
|                   ^^^^^^^^^^^^^^ expected function that takes 2 arguments
...
39 | async fn handle_request(params: Params) -> Result<impl warp::Reply, warp::Rejection> {
| ------------------------------------------------------------------------------------ takes 1 argument
|
= note: required because of the requirements on the impl of `warp::generic::Func<((), Params)>` for `fn(Params) -> impl Future {handle_request}`

这些是已使用的依赖项:

[dependencies]
log = "0.4"
pretty_env_logger = "0.4"
tokio = { version = "1", features = ["full"] }
warp = "0.3"
serde = { version = "1.0", features = ["derive"] }
futures = { version = "0.3", default-features = false, features = ["alloc"] }

只需使您的方法不提取任何内容:

async fn handle_request(params: Params) -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder().body(format!("key1 = {}, key2 = {}", params.key1, params.key2)))
}

fn key_validation() -> impl Filter<Extract = (), Error = Rejection> + Copy {
warp::header::<String>("x-api-key").and_then(|n: String| async move {
if n == "test" {
Ok(())
} else {
Err(reject::custom(Unauthorized))
}
})
}

您可能需要放弃key_validation结果值,使用untuple_one:

let route1 = warp::get()
.and(key_validation())
.untuple_one()
.and(warp::query::<Params>())
.and_then(handle_request);

最新更新