2 个版本

0.1.1 2022 年 7 月 9 日
0.1.0 2022 年 7 月 9 日

#5#authorization-header

Download history 2/week @ 2024-03-23 39/week @ 2024-03-30 5/week @ 2024-04-06 4/week @ 2024-04-20 1/week @ 2024-04-27 15/week @ 2024-05-11 2/week @ 2024-05-18 9/week @ 2024-05-25 19/week @ 2024-06-01 1/week @ 2024-06-08

每月 119 次下载

MIT 许可证

10KB
82

tide-jwt

tide-jwt 是 tide 网页框架的 JWT 授权中间件的简单实现。它使用 jsonwebtoken crate 进行编码/解码。如果发现存在无效的 "Authorization" 头部,它将只返回未授权。如果没有找到授权头部,中间件将继续运行。确保请求确实经过认证是实施者的责任,以防止下游中间件运行并返回适当的响应。

特性

[x] 读取 "Authorization" 头部 [x] 使用泛型声明和 jsonwebtoken 验证 "Bearer" 令牌 [x] 提供编码辅助函数(从密钥 base64、选择的算法、声明) [x] 支持 Send + Sync + 'static, 可序列化和反序列化 (serde) 声明,用于 jsonwebtoken [] 可能读取配置的 jwt 令牌 [x] 支持非 jwt (jose 规范)

示例

使用 tide 网页框架的实现非常简单,只需使用 .with 函数包含中间件即可。这在技术上相当于一个 Before 中间件,它在继续其他中间件之前从 Request 中读取。它将使用 set_ext 函数添加获取 <Claims> 对象的能力,并将其作为扩展添加到请求上。

use jsonwebtoken::{DecodingKey, Validation};
use registry::State;
use serde::{Deserialize, Serialize};
use tide::log::LogMiddleware;
use tide_jwt::JwtAuthenticationDecoder;

mod flash;
mod registry;
mod routes;

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    sub: String,
    username: String,
    uid: u64,
    exp: usize,
}

#[async_std::main]
async fn main() -> tide::Result<()> {
    let mut app = tide::with_state(State::new());
    dotenv::dotenv().ok();
    env_logger::init();

    app.with(LogMiddleware::new());

    // configure openid connect and session middleware
    let jwt_secret = std::env::var("SESSION_SECRET")?;
    app.with(JwtAuthenticationDecoder::<Claims>::new(
        Validation::default(),
        DecodingKey::from_base64_secret(&jwt_secret)?,
    ));
    routes::configure(&mut app);

    let host = std::env::var("HOST").unwrap_or(String::from("0.0.0.0"));
    let port: u16 = std::env::var("PORT")?.parse()?;
    app.listen((host, port)).await?;

    Ok(())
}

上面的示例将允许中间件正确解码请求中的 Claims 并将其作为扩展设置在请求上。这使得我们可以在端点或其他中间件函数中稍后抓取它。

pub async fn index(req: Request<State>) -> tide::Result {
    let claims = req.ext::<Claims>();
    println!("{:?}", claims);

    let mut res = Response::new(200);
    Ok(res)
}

假设您已正确认证了请求,您可以使用 encode 实用函数来编码带有正确声明的令牌。

pub async fn login(mut req: Request<State>) -> tide::Result {
    match req.body_form::<UserForm>().await {
        Ok(form) => {
            if form.username == "foo" && form.password == "bar" {
                let mut res: Response = Redirect::new("/").into();
                let secret = std::env::var("SESSION_SECRET")?;
                let claims = Claims {
                    username: String::from("foo"),
                    exp: 10000000000,
                    sub: String::from("asdf"),
                    uid: 1,
                };
                let token = tide_jwt::jwtsign_secret(&claims, &secret)?;
                println!("{token}");
                res.insert_cookie(
                    Cookie::build("_jwt", token)
                        .max_age(Duration::seconds(60000))
                        .same_site(SameSite::Lax)
                        .path("/")
                        .finish(),
                );
                Ok(res)
            } else {
                flash::redirect("/", flash::warn("invalid credentials"))
            }
        }
        Err(e) => flash::redirect("/", flash::error(e.to_string().as_str())),
    }
}

声明应该构建为允许某些唯一性(例如 exp 或 iat),以便每次执行加密/签名时都具有唯一性。需要注意的是,通常使用会话进行 Web 应用的认证,而 JWT 通常保留用于后端 api 服务(或者如果您正在考虑无服务器类型的生产系统)。

依赖关系

~15–27MB
~525K SLoC