axum 集成 JWT

1178941
2021/11/13 08:14:33

定义密钥

密钥用于对认证信息进行加密、解密。

const JWT_SECRET: &str = "https://AXUM.RS";

pub struct Keys {
    encoding: EncodingKey,
    decoding: DecodingKey<'static>,
}

impl Keys {
    pub fn new(secret: &[u8]) -> Self {
        Self {
            encoding: EncodingKey::from_secret(secret),
            decoding: DecodingKey::from_secret(secret).into_static(),
        }
    }
    pub fn global() -> Self {
        Self::new(JWT_SECRET.as_bytes())
    }
}

定义传递的数据

#[derive(Serialize, Deserialize)]
pub struct Claims {
    pub sub: String,
    pub company: String,
    pub exp: usize,
}

定义一些辅助的结构体

请求获取令牌的结构体

#[derive(Deserialize)]
pub struct AuthPayload {
    pub client_id: String,
    pub client_secret: String,
}

返回给客户端的令牌结构体

#[derive(Serialize)]
pub struct AuthBody {
    pub access_token: String,
    pub token_type: String,
}

impl AuthBody {
    pub fn new(access_token: String) -> Self {
        Self {
            access_token,
            token_type: String::from("Bearer"),
        }
    }
}

获取授权令牌

async fn authorize(Json(payload): Json<AuthPayload>) -> Result<Json<AuthBody>, Json<String>> {
    if payload.client_id.is_empty() || payload.client_secret.is_empty() {
        return Err(Json(String::from("Missing Credentials")));
    }

    if payload.client_id != "axum.rs" || payload.client_secret != "[email protected]" {
        return Err(Json(String::from("Wrong Credentials")));
    }

    let claims = Claims {
        sub: "[email protected]".to_string(),
        company: "AXUM.RS".to_string(),
        exp: 10000000000,
    };

    let token = encode(&Header::default(), &claims, &Keys::global().encoding)
        .map_err(|err| Json(err.to_string()))?;

    Ok(Json(AuthBody::new(token)))
}

需要令牌才能访问的资源

async fn protected(
    TypedHeader(Authorization(bearer)): TypedHeader<Authorization<Bearer>>,
) -> Result<String, String> {
    let token_data = decode::<Claims>(
        bearer.token(),
        &Keys::global().decoding,
        &Validation::default(),
    )
    .map_err(|err| format!("Invalid Token: {}", err.to_string()))?;
    let claims = token_data.claims;
    Ok(format!(
        "Welcome, your email {}, company: {}",
        claims.sub, claims.company
    ))
}

运行

获取令牌

$ curl -i -X POST -H 'content-type:application/json' -d '{"client_id":"axum.rs","client_secret":"[email protected]"}' http://127.0.0.1:9527/authorize
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZWFtQGF4dW0ucnMiLCJjb21wYW55IjoiQVhVTS5SUyIsImV4cCI6MTAwMDAwMDAwMDB9.2jPYCuK6_nDrFdXS3HLAm43YvbFvrBBLYS6YkZ_z6zM","token_type":"Bearer"}

使用令牌访问被保护的数据

使用非法的令牌访问被保护的数据

curl -H 'content-type:application/json' -H 'Authorization: Bearer foobar' 127.0.0.1:9527/protected
Invalid Token: InvalidSignature

本文讨论了在 axum 集成 JWT 功能,完整代码可以在我们的代码仓库中找到。