Rust Web Rokcet 系列 3 使用 JWT

JWT即使用的包是jsonwebtoken。是使用token进行验证的一种方式。基于token的认证方式相比传统的session认证方式相比节约服务器资源。 JWT 官网 https://jwt.io/

本次使用的包是jsonwebtoken。代码基于本系列第二篇文章 ,系列地址 https://www.ftls.xyz/series/rust-web-rokcet/

Cargo.toml 增加依赖

1
jsonwebtoken = "8.0.1"

src\module.rs 增加代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Claims {
    pub sub: String,
    pub exp: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(crate = "rocket::serde")]
pub struct UserAuth {
    pub id: i32,
    pub key: String,
}

增加 src\auth.rs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
use crate::module::Claims;
use jsonwebtoken::{decode, DecodingKey, Validation};
use rocket::http::Status;
use rocket::request::{FromRequest, Outcome};

pub const KEY: &[u8] = b"secret";

pub struct Token;
// Bearer Token
impl Token {
    fn from_request(header: &str) -> Option<Token> {
        let split_vec = header.split_whitespace().collect::<Vec<_>>();
        if split_vec.len() != 2 {
            return None;
        }
        if split_vec[0] != "Bearer" {
            return None;
        }
        Self::from_jwt(split_vec[1])
    }
    fn from_jwt(token_string: &str) -> Option<Token> {
        let mut val = Validation::default();
        val.sub = Some("!Yg43#xQtBE357js".to_string());
        match decode::<Claims>(token_string, &DecodingKey::from_secret(KEY), &val) {
            Ok(c) => {
                println!("ExpTime:{:?}", c.claims.exp);
                return Some(Token);
            }
            Err(_) => None,
        }
    }
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for Token {
    type Error = ();
    async fn from_request(request: &'r rocket::Request<'_>) -> Outcome<Self, Self::Error> {
        let header_auth = request.headers().get_one("Authorization");
        if let Some(header_auth) = header_auth {
            if let Some(auth) = Self::from_request(header_auth) {
                return Outcome::Success(auth);
            }
        }
        Outcome::Failure((Status::Unauthorized, ()))
    }
}

src\routes.rs 增加代码: 其他路由使用 Token 验证只需要在函数中添加变量 _auth: Token 即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
use crate::auth::{Token, KEY};
use crate::module::{Claims, UserAuth};
use jsonwebtoken::{encode, EncodingKey, Header};
use std::time::{SystemTime, UNIX_EPOCH};

// get token
#[post("/token", format = "json", data = "<user_auth>")]
pub async fn get_token(user_auth: Json<UserAuth>) -> Value {
    let user = user_auth.into_inner();
    if user.id == 0 && user.key.eq("oR66T*W8y4VaXkh#rTjeZ$$Rby$NCy!nJX") {
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();
        let token = match encode(
            &Header::default(),
            &Claims {
                sub: String::from("!Yg43#xQtBE357js"),
                exp: timestamp + 5,
            },
            &EncodingKey::from_secret(KEY),
        ) {
            Ok(t) => t,
            Err(_) => panic!(),
        };
        json!({ "token": token })
    } else {
        json!({"token": "Auth Fail"})
    }
}

// get token test
#[get("/token/test")]
pub async fn get_token_test(_auth: Token) -> Value {
    json!({"status":"Auth Success"})
}

src\main.rs 增加

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16

mod auth;

#[launch]
fn rocket() -> _ {
    rocket::build()
        // database
        .attach(MainDbConn::fairing())
        .mount("/", routes![index])
        // add api
        .mount("/", routes![get_all_articles, get_article_by_id])
        .mount("/", routes![post_article, put_article])
        .mount("/", routes![delete_all_articles, delete_article])
        // token 本文新增的
        .mount("/", routes![get_token, get_token_test])
}

测试 http://127.0.0.1:8000/token/test 方法,

该请求的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const gettoken = {
    url: ' http://127.0.0.1:8000/token',
    method: "POST",
    header: 'Content-Type: application/json',
    body: {
        mode: 'raw', 
    raw: JSON.stringify({"id": 0, "key": "oR66T*W8y4VaXkh#rTjeZ$$Rby$NCy!nJX"}) 
		}
}
pm.sendRequest(gettoken, function (err, response) {
    console.log(response.json().token);
    pm.collectionVariables.set("token",response.json().token);
});

在该请求的 Header 中添加键Authorization ,值 Bearer {{token}}。

https://cdn.ftls.xyz/images/2022/02/20220216132819.png
Postman测试

这之后就可以正常请求了。

Postman 分享链接:

https://www.getpostman.com/collections/c89ec512876818f18757

如果链接失效了,请只会一声。