首页 > 解决方案 > 我应该如何在 rust 中强制执行 jsonwebtokens 的反序列化类型?

问题描述

以下是我使用 jsonwebtoken 创建的令牌创建工具。我想以某种方式强制执行预期的令牌类型,这样如果我传入一个令牌字符串并告诉它我期望的声明集,它就不会返回成功的结果。

下面包括测试用例,其中包含关于我认为该服务应该在哪里失败的注释,以及我认为应该发生断言的代码中的注释。

如何强制执行这些声明类型以确保获得所需的令牌类型?

use jwt;
use jwt::{ Header, Validation };
use std::convert::AsRef;
use serde::de::DeserializeOwned;
use serde::Serialize;

#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum TokenType {
    User,
    Reg,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct RegisterClaims {
    typ:TokenType,
    org_name:String,
    name:String,
    email:String,
    exp: usize,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct UserClaims {
    typ:TokenType,
    usr_id:String,
    sub:String,
    exp: usize,
}

#[derive(Debug)]
pub struct InvalidToken {
    cause: String,
}

pub struct TokenFactory {
    secret:String,
}

impl TokenFactory {
    pub fn new(secret:String) -> TokenFactory {
        TokenFactory {
            secret
        }
    }

    pub fn validate<T: DeserializeOwned>(&self, raw_token:String) -> Result<T, InvalidToken> {
        match jwt::decode::<T>(&raw_token, self.secret.as_ref(), &Validation::default()) {
            Ok(tokendata) => {
                /*
                some how assert the type of T to match and return an Err if not matched
                What
                */
                Ok(tokendata.claims)
            },
            Err(err) => {
                // todo: in the future check error kind and give better errors
                Err(InvalidToken{
                    cause: err.to_string()
                })
            }
        }
    }

    pub fn mint_token<T: Serialize>(&self, claims:&T) -> String {
        jwt::encode(&Header::default(), claims, self.secret.as_ref()).unwrap()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util;
    use std::borrow::Borrow;

    #[test]
    fn test_valid() {
        let usr = UserClaims {
            typ: TokenType::User,
            sub:"foobar@gmail.com".to_string(),
            exp:util::current_time_secs()+1,
            usr_id:"usr-1234".to_string(),
        };

        let tf = TokenFactory::new("my_sceret".to_string());

        let token = tf.mint_token(usr.borrow());

        let usr_claims: UserClaims = tf.validate(token).unwrap();

        assert_eq!(usr.sub, usr_claims.sub);
    }

    #[test]
    fn test_mixed() {
        let reg = RegisterClaims {
            typ:TokenType::Reg,
            org_name:"foo".to_string(),
            name:"bar".to_string(),
            email:"foobar@inc".to_string(),
            exp:util::current_time_secs()+1,
        };

        let tf = TokenFactory::new("my_sceret".to_string());

        let token = tf.mint_token(reg.borrow());

        let usr_claims: UserClaims = tf.validate(token).unwrap(); // want it to fail here

        assert_eq!(reg, usr_claims); // fails here
    }
}

标签: rustjwtdeserializationserde

解决方案


我最终使用的解决方案是为我拥有的每个声明结构实施以下训练。

trait ClaimType {
    fn is_type() -> TokenType;
    fn has_type(&self) -> TokenType;
}

然后在我的验证中我做了

if T::is_type() == tokendata.claims.has_type() {
    return Ok(tokendata.claims);
}

Err(InvalidToken{
    cause: "Wrong token type".to_string()
})

我确定可能有一种方法可以使用宏来为我实现特征,或者让库本身强制执行一些反序列化检查。但是上面完成了


推荐阅读