在設計HTTP API的時候,為了使API不被任何人(應用程式)使用,並且可以一定程度地知道發送請求的客戶端是誰,就必須要有個授權機制。
Simple-Authorization Request Guard for Rocket Framework
「Simple-Authorization Request Guard for Rocket Framework」是筆者開發的套件,利用特性和巨集,可以快速地將任意型別實作成請求守衛,這個請求守衛會自動去讀取HTTP請求中的Authorization
標頭欄位。
Crates.io
Cargo.toml
使用方法
使用這個套件來實作授權機制可以分為四個步驟。
步驟一:定義一個結構體或是列舉,並實作它的方法,使它之後可以方便地被用來當作請求守衛來使用。注意,在這個步驟先不要自行實作rocket::request::FromRequest
特性,因為這個套件就是為了要避開自行實作rocket::request::FromRequest
特性所帶來的麻煩,如果自行實作rocket::request::FromRequest
特性就沒有意義了。
舉例來說:
pub struct Auth {
account: String
}
impl Auth {
pub fn as_str(&self) -> &str {
self.auth_data.as_str()
}
}
我們希望以上這個Auth
結構體,可以在處理HTTP請求時,去檢查Authorization
標頭欄位中的值是否為某個使用者的記號(token),檢查成功後,account
欄位會將使用者的帳號儲存下來。而這裡的使用者的記號其實就是使用者的帳號經過ShortCrypt加密之後的值。
步驟二:實作rocket_simple_authorization
這個crate提供的SimpleAuthorization
特性。SimpleAuthorization
特性只有一個方法需要實作,那就是authorizing
。authorizing
方法要負責檢查Authorization
標頭欄位中儲存的使用者記號,將其解密成使用者帳號之後,建立出Auth
結構實體並回傳。
程式如下:
use rocket::request::Request;
use rocket_simple_authorization::SimpleAuthorization;
#[async_trait]
impl<'r> SimpleAuthorization<'r> for Auth {
async fn authorizing(request: &'r Request<'_>, authorization: Option<&'r str>) -> Option<Self> {
let sc = request.rocket().state::<ShortCrypt>().unwrap();
match authorization {
Some(authorization) => {
match sc.decrypt_url_component(authorization) {
Ok(result) => {
match String::from_utf8(result) {
Ok(user_name) => {
Some(Auth {
auth_data: user_name,
})
}
Err(_) => None,
}
}
Err(_) => None,
}
}
None => None,
}
}
}
以上程式,利用rocket::request::Request
結構實體的guard
方法來取得儲存在應用程式狀態中的ShortCrypt
。
步驟三:利用authorizer
巨集,將實作好SimpleAuthorization
特性的結構體或是列舉做成請求守衛。
程式如下:
authorizer!(Auth);
如果要使請求守衛支援Rocket框架的請求狀態,可以將程式寫成:
authorizer!(ref Auth);
一般來說,權限相關等會需要進行加解密、存取資料庫的請求守衛,都需要利用請求狀態來避免被執行多次。
步驟四:將請求守衛應用在路由處理程序的參數中。
例如:
#[get("/time")]
fn system_time(auth: &Auth) -> Option<String> {
match auth.as_str() {
"magiclen.org" => (),
_ => return None
}
let utc: DateTime<Utc> = Utc::now();
Some(utc.format("%Y-%m-%d-%H-%M-%S").to_string())
}
以上程式,只有當使用者帳號為magiclen.org
時,才可以正常地使用GET /time
這支HTTP API。