获取访问令牌及调用API

获取访问令牌

首先,我们需要对 access_token 进行修改,主要是删除中间件部分:

// src/authorize/handler.rs

pub async fn access_token(
    State(state): State<ArcAppState>,
    Json(frm): Json<payload::NewAccessToken>,
) -> Result<Json<token_model::Token>> {
    frm.validate()?;

    let mut tx = state.pool.begin().await?;

    // 临时令牌
    let tmp_token = match token_db::find(&mut *tx, token_db::FindBy::Token(&frm.code)).await {
        Ok(v) => match v {
            Some(v) => v,
            None => {
                return Err(Error::not_found("授权码不存在"));
            }
        },
        Err(e) => {
            tx.rollback().await?;
            return Err(e.into());
        }
    };

    // 应用密钥
    let app_secret =
        match application_secret::db::find_by_secret(&mut *tx, &frm.client_secret, &frm.client_id)
            .await
        {
            Ok(v) => match v {
                Some(v) => v,
                None => {
                    return Err(Error::not_found("客户ID/密钥错误"));
                }
            },
            Err(e) => {
                tx.rollback().await?;
                return Err(e.into());
            }
        };
    if app_secret.application_id != tmp_token.application_id {
        return Err(Error::forbidden("授权应用不匹配"));
    }
    if app_secret.secret != frm.client_secret {
        return Err(Error::not_found("客户ID/密钥错误"));
    }

    // 访问令牌
    let t = token_model::Token::try_with_application(
        &tmp_token.user_id,
        state.cfg.access_token_expired,
        token_model::TokenKind::AccessToken,
        &tmp_token.application_id,
    )?;

    let t = match token_db::create(&mut *tx, t).await {
        Ok(v) => v,
        Err(e) => {
            tx.rollback().await?;
            return Err(e.into());
        }
    };

    // 删除临时令牌
    if let Err(e) = token_db::delete_by_token(&mut *tx, &frm.code).await {
        tx.rollback().await?;
        return Err(e.into());
    }
    tx.commit().await?;

    Ok(Json(t))
}

然后调整路由:

// src/init/router.rs
pub fn init(state: ArcAppState) -> Router {
    Router::new()
        .nest("/api", api_init(state.clone()))
        .nest("/login/oauth", access_token_init(state))
}

fn access_token_init(state: ArcAppState) -> Router {
    Router::new()
        .route("/access_token", post(authorize::handler::access_token))
        .with_state(state)
}

获取访问令牌:

// examples/access_token.rs

use oauth2::token;

#[derive(serde::Serialize)]
pub struct AccessTokenRequest {
    pub code: String,
    pub client_secret: String,
    pub client_id: String,
}

#[derive(serde::Deserialize, Debug)]
pub struct Token {
    pub id: String,
    pub user_id: String,
    pub token: String,
    pub kind: token::model::TokenKind,
    pub application_id: String,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub expired_at: chrono::DateTime<chrono::Utc>,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let code = std::env::var("CODE")?; // 临时令牌
    let client_secret = std::env::var("CLIENT_SECRET")?; // 应用密钥
    let client_id = std::env::var("CLIENT_ID")?; // 应用ID

    let data = AccessTokenRequest {
        code,
        client_secret,
        client_id,
    };

    let access_token: Token = reqwest::Client::new()
        .post("http://127.0.0.1:9527/login/oauth/access_token")
        .json(&data)
        .send()
        .await?
        .json()
        .await?;

    println!("{:#?}", access_token);

    Ok(())
}

运行:

CLIENT_ID=d7i3***m50 CLIENT_SECRET=583d***ce2b CODE=f545***f2f cargo run --example access_token

结果:

Token {
    id: "d7i4***b10",
    user_id: "d7g4***gc0",
    token: "b456***9b7",
    kind: AccessToken,
    application_id: "d7i3***m50",
    created_at: 2026-04-19T03:07:31.649378Z,
    expired_at: 2026-04-19T04:07:31.649379Z,
}

拿到访问令牌后,我们可以通过 API 获取用户信息了。

首先是 handler:

// src/user/handler.rs

pub async fn find(
    State(state): State<ArcAppState>,
    user_auth: UserAuth,
) -> Result<Json<model::User>> {
    // Ok(Json(user_auth.user)) // 1️⃣
    let user = match db::find(&state.pool, db::FindBy::Id(&user_auth.user.id)).await? {
        Some(v) => v,
        None => return Err(Error::not_found("用户不存在")),
    }; // 2️⃣

    Ok(Json(user))
}
  • 1️⃣ 我们可以直接从中间件里获取当前用户的信息。但是某些情况我们可能还是需要从数据库查询最新的数据。具体实现取决于系统的要求。
  • 2️⃣ 从数据库中查询最新的数据。
// src/init/router.rs

fn api_init(state: ArcAppState) -> Router {
    Router::new()
        .nest("/auth", auth_init(state.clone()))
        .nest("/application", application_init(state.clone()))
        .nest("/login/oauth", authorize_init(state.clone()))
        .nest("/user", user_init(state))
}

fn user_init(state: ArcAppState) -> Router {
    Router::new()
        .route("/", get(user::handler::find))
        .layer(middleware::from_extractor_with_state::<
            mw::UserAuth,
            ArcAppState,
        >(state.clone()))
        .with_state(state)
}

获取用户信息:

// examples/user_api.rs

use oauth2::user::model;

#[derive(Debug, serde::Deserialize)]
pub struct User {
    pub id: String,
    pub username: String,
    pub email: String,
    pub status: model::UserStatus,
    pub created_at: chrono::DateTime<chrono::Utc>,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let access_token = std::env::var("ACCESS_TOKEN")?; // 访问令牌

    let client = reqwest::Client::new();
    let user: User = client
        .get("http://127.0.0.1:9527/api/user")
        .bearer_auth(access_token)
        .send()
        .await?
        .json()
        .await?;

    println!("{:#?}", user);
    Ok(())
}

运行:

User {
    id: "d7g4***c0",
    username: "axum",
    email: "[email protected]",
    status: Active,
    created_at: 2026-04-16T02:55:03.985071Z,
}

本章代码位于06.AccessTokenAndAPI分支。

要查看完整内容,请先登录