首页 > 其他分享 >axum 实现 Session

axum 实现 Session

时间:2023-11-02 10:05:09浏览次数:43  
标签:session axum err 实现 Session cookie let id

由于 HTTP 是无状态的,所以我们可以通过cookie来维护状态。但 cookie 是直接保存到客户端,所以对于敏感数据,不能直接保存到 cookie。我们可以把敏感数据保存到服务端,然后把对应的 ID 保存到 cookie,这就是 Session。本章我们将使用 Cookie 和 Redis 实现一个简单的 Session。

本章将会通过一个简单的用户登录流程来演示 Session 的实现。

  • GET /:用户信息首页。如果登录成功,从 Session 中读取已登录用户的信息;如果没有登录,提示用户进行登录

  • GET /login:显示用户登录表单

  • POST /login:处理用户登录。如果用户名和密码正确,将用户信息保存到 Session,并跳转到用户信息首页。

  • GET /logout:退出登录。清空 Session

保存 Session 的流程

将用户信息保存到redis,并将Session ID写入cookie

代码如下:

/// 将 Session ID 保存到 Cookie
fn save_session_id_to_cookie(session_id: &str, headers: &mut HeaderMap) {
    let cookie = format!("{}={}", SESSION_ID_COOKIE_NAME, session_id);
    headers.insert(
        axum::http::header::SET_COOKIE,
        cookie.as_str().parse().unwrap(),
    );
}

读取 Session 的流程

从cookie中读取到Session ID,然后从 Redis 读取该ID对应的用户信息

代码如下:

/// 从 cookie 中获取session id
fn get_session_from_cookie(headers: &HeaderMap) -> Option<String> {
    let cookies = headers
        .get(axum::http::header::COOKIE)
        .and_then(|value| value.to_str().ok())
        .unwrap_or("");
    if cookies.is_empty() {
        return None;
    }
    let mut session_id: Option<String> = None;
    let cookies: Vec<&str> = cookies.split(';').collect();
    for cookie in cookies {
        let cookie_pair: Vec<&str> = cookie.split('=').collect();
        let cookie_name = cookie_pair[0].trim();
        let cookie_value = cookie_pair[1].trim();
        if cookie_name == SESSION_ID_COOKIE_NAME && !cookie_value.is_empty() {
            session_id = Some(cookie_value.to_string());
            break;
        }
    }
    session_id
}

登录

首先,我们来看一下登录操作。获取用户提交的表单,并判断用户名和密码,如果验证通过,将用户信息序列化后保存到 Redis,并生成对应的 Session ID。将这个 Session ID 写入 Cookie,同时跳转到用户信息首页:

/// 登录操作
async fn logout_action(
    Extension(rdc): Extension<redis::Client>,
    Form(frm): Form<UserLoginForm>,
) -> Result<(StatusCode, HeaderMap, ()), String> {
    let mut headers = HeaderMap::new();
    let url;
    if !(&frm.username == "axum.rs" && &frm.password == "axum.rs") {
        url = "/login?msg=用户名或密码错误"
    } else {
        // 生成 session ID
        let session_id = Uuid::new_v4().to_simple().to_string();
        // 将 session ID 保存到 Cookie
        save_session_id_to_cookie(&session_id, &mut headers);

        let user_session = UserSession {
            username: frm.username,
            level: 1,
        };
        let user_session = serde_json::json!(user_session).to_string();

        // 将 session 保存到 redis
        let redis_key = format!("{}{}", SESSION_KEY_PREFIX, session_id);
        let mut conn = rdc
            .get_async_connection()
            .await
            .map_err(|err| err.to_string())?;
        // session 将在20分钟后自动过期
        conn.set_ex(redis_key, user_session, 1200)
            .await
            .map_err(|err| err.to_string())?;
        url = "/"
    }
    headers.insert(axum::http::header::LOCATION, url.parse().unwrap());
    Ok((StatusCode::FOUND, headers, ()))
}

用户信息首页

在用户信息首页,首先尝试从 Cookie 中获取 Session ID,获取到之后,通过这个 Session ID 从 Redis 读取出用户信息,并反序列化为结构体。如果 Cookie 中没有 Session ID 或者 Redis 中没有对应的用户信息,则提示需要登录。

/// 首页
async fn index(
    Extension(rdc): Extension<redis::Client>,
    headers: HeaderMap,
) -> Result<Html<String>, String> {
    let session_id = get_session_from_cookie(&headers);
    let mut session: Option<UserSession> = None;
    if let Some(session_id) = session_id {
        // 从 redis 读取 Session
        let redis_key = format!("{}{}", SESSION_KEY_PREFIX, session_id);
        let mut conn = rdc
            .get_async_connection()
            .await
            .map_err(|err| err.to_string())?;
        let session_str: Option<String> =
            conn.get(redis_key).await.map_err(|err| err.to_string())?;
        if let Some(session_str) = session_str {
            let user_session: UserSession =
                serde_json::from_str(&session_str).map_err(|err| err.to_string())?;
            session = Some(user_session);
        }
    }
    match session {
        Some(session) => {
            let html = format!(
                r#"
        <!DOCTYPE html>
        <html lang="zh-Hans">
          <head>
            <meta charset="utf-8" />
            <meta name="author" content="axum.rs ([email protected])" />
            <title>
              用户首页-AXUM中文网
            </title>
          </head>
          <body>
            <div>欢迎 {} ! 你的等级是 {}。</div>
            <div><a href="/logout">退出登录</a></div>
          </body>
          </html>"#,
                session.username, session.level
            );
            Ok(Html(html))
        }
        None => Err("Please login via /login page".to_string()),
    }
}

退出登录

首先从 Cookie 中获取到 Session ID,然后将对应的用户信息从 Redis 中删除。

/// 退出登录
async fn logout(
    Extension(rdc): Extension<redis::Client>,
    headers: HeaderMap,
) -> Result<(StatusCode, HeaderMap, ()), String> {
    let session_id = get_session_from_cookie(&headers);
    let mut headers = HeaderMap::new();
    if let Some(session_id) = session_id {
        // 从 redis 删除 Session
        let redis_key = format!("{}{}", SESSION_KEY_PREFIX, session_id);
        let mut conn = rdc
            .get_async_connection()
            .await
            .map_err(|err| err.to_string())?;
        conn.del(redis_key).await.map_err(|err| err.to_string())?;
        // 清空Cookie
        save_session_id_to_cookie(&session_id, &mut headers);
    }
    headers.insert(axum::http::header::LOCATION, "/login".parse().unwrap());
    Ok((StatusCode::FOUND, headers, ()))
}

本章我们讨论了如何利用 Cookie 和 Redis 实现一个简单的 Session。涉及的代码有点多,请通过我们的代码仓库查看完整代码。

标签:session,axum,err,实现,Session,cookie,let,id
From: https://www.cnblogs.com/pythonClub/p/17804724.html

相关文章

  • axum 集成 JWT
    Jsonwebtoken(JWT)是为了网络应用环境间传递声明而执行的一种基于JSON的开发标准(RFC7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加......
  • axum中的各种响应
    本章主要讨论axum的响应。axum已经实现了多种响应,比如纯文本、HTML、JSON及自定义响应头(responseheader)。除了这些axum内置的响应之外,我们还将讨论如何将自己定义的结构体,作为响应返回给客户端。axum的响应axum有句话说的是:Anythingthatimplements IntoRespons......
  • 在axum中获取请求数据
    在日常开发中,我们需要与用户进行交互,从各种渠道获取用户输入,包括但不限于:表单、URL参数、URLPath以及JSON等。axum为我们提供了这些获取用户输入的支持。获取 Path 参数Path 参数,又称为“路径参数”,它既可以实现参数的传递,又对SEO友好。什么是 Path 参数假设有以......
  • axum 操作 Postgres 数据库
    PostgreSQL是一款天然支持异步操作的高性能开源关系型数据库。本章将讨论如何在axum中使用PostgreSQL。包括:数据的增加、修改、删除、查找以及开始事务保证业务的原子性。如果你对PostgreSQL不是很了解,可以通过PostgreSQL轻松学网站进行学习。ElephantSQL提供了免费的Po......
  • axum处理cookie
    Cookie是通过HTTPHeader进行传递的。由某个响应头进行设置,然后其它请求头就可以获取到了。本章将通过模拟用户中心来用axum操作HTTPHeader演示Cookie的读写操作。本章示例将实现以下路由:路由说明GET/用户中心首页。如果用户未登录,显示提示信息;如果用户已登......
  • axum 操作 redis
    通过 redis-rs 这个crate,可以很方便的操作redis。它提供了同步和异步两种连接,由于我们要集成到axum中,所以这里使用异步连接。本章将展示如何获取redis异步连接、如何将字符串保存到redis、如何获取到保存在redis里的字符串以及如何通过redis保存和读取自定义结构体。......
  • axum处理静态文件
    和其它Web框架一样,axum也会对所有请求进行处理。对于CSS、JS及图片等静态文件,并不需要axum的handler进行处理,而是只需要简单的把它们的内容进行返回即可。axum提供了处理静态文件的中间件。首先,我们创建一个名为 static 的目录,并在其中创建一个 axum-rs.txt 的文......
  • axum的状态共享
    状态共享是指,在整个应用或不同路由之间,共享一份数据。axum提供了方便的状态共享机制,但可能也会踩坑。本章将带你学习如何在axumweb应用中共享状态。如何进行状态共享axum使用 Layer 来实现状态共享。定义路由时,使用 layer() 加入要共享的数据,在需要获取该共享数据的......
  • 手撕Vuex-实现actions方法
    经过上一篇章介绍,完成了实现mutations的功能,那么接下来本篇将会实现actions的功能。本篇我先介绍一下actions的作用,然后再介绍一下实现的思路,最后再实现代码。actions的作用是用来异步修改共享数据的,怎么异步修改,这个时候我们回到Vue的官方Vuex文档中,有如下这么一......
  • m基于FPGA的8FSK调制解调系统verilog实现,包含testbench测试文件
    1.算法仿真效果vivado2019.2仿真结果如下:    将波形放大,看到如下效果:    2.算法涉及理论知识概要       8FSK(8-FrequencyShiftKeying)是一种常用的数字调制方法,它通过在不同的频率上发送二进制数据来进行通信。8FSK在通信系统中被广泛应用,因为它具......