- 支持试读
认识 OAuth 2.0
OAuth 2.0 是授权协议的行业标准,允许应用程序在无需共享密码的情况下获取对用户数据的有限、安全访问权限。 接入 Github OAuth 2.0
开发自己的 OAuth 服务之前,通过集成第三方的 OAuth 服务能更加了解其流程,本章我们将通过接入 Github 的 OAuth 服务来体验 OAuth 的完整流程。申请接入
本章我们开始实现自己的 OAuth 2.0 服务,首先从申请接入开始。应用管理
本章我们实现应用及密钥的管理。- 支持试读
用户授权
本章我们将实现用户授权。 - 支持试读
获取访问令牌及调用API
本章我们将实现访问令牌的获取以及用户信息API。
获取访问令牌及调用API
- 7
- 2026-04-19 11:45:42
获取访问令牌
首先,我们需要对 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分支。
