在設計HTTP API的時候,為了使API不被任何人(應用程式)使用,並且可以一定程度地知道發送請求的客戶端是誰,就必須要有個授權機制。



Simple-Authorization Request Guard for Rocket Framework

「Simple-Authorization Request Guard for Rocket Framework」是筆者開發的套件,利用特性和巨集,可以快速地將任意型別實作成請求守衛,這個請求守衛會自動去讀取HTTP請求中的Authorization標頭欄位。

Crates.io

https://crates.io/crates/rocket-simple-authorization

Cargo.toml

rocket-simple-authorization = "*"

使用方法

使用這個套件來實作授權機制可以分為四個步驟。

步驟一:定義一個結構體或是列舉,並實作它的方法,使它之後可以方便地被用來當作請求守衛來使用。注意,在這個步驟先不要自行實作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特性只有一個方法需要實作,那就是authorizingauthorizing方法要負責檢查Authorization標頭欄位中儲存的使用者記號,將其解密成使用者帳號之後,建立出Auth結構實體並回傳。

程式如下:

use rocket::request::Request;
use rocket_simple_authorization::SimpleAuthorization;

impl<'a, 'r> SimpleAuthorization<'a, 'r> for Auth {
    fn authorizing(request: &'a Request<'r>, authorization: Option<&'a str>) -> Option<Self> {
        let sc = request.guard::<State<ShortCrypt>>().unwrap();

        match authorization {
            Some(authorization) => {
                match sc.decrypt_url_component(authorization) {
                    Ok(result) => {
                        match String::from_utf8(result) {
                            Ok(account) => Some(Auth {
                                account
                            }),
                            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。