我对 rust 还很陌生,正在玩 axum。 我想使用 PKCE 实现授权代码流程。因此,我必须将生成的 pkce 代码验证程序移交给回调路由,以便将代码交换为令牌,以便我可以继续使用会话等方式登录我的用户。
现在我主要使用锤子,只是为了描述为什么解决方案看起来像这样:
#[derive(Clone)]
struct AppState {
db: PgPool,
oauth_client: BasicClient,
verifiers: Arc<Mutex<HashMap<String, String>>>,
}
#[debug_handler]
async fn callback(
State(state): State<AppState>,
Query(auth_request): Query<AuthRequest>,
) -> Result<impl IntoResponse, impl IntoResponse> {
let auth_request = auth_request;
let verifiers = state.verifiers.lock().unwrap();
let pkce_verifier = verifiers.get(&auth_request.state).unwrap().into();
let pkce_verifier = PkceCodeVerifier::new(pkce_verifier);
let _token_result = match state
.oauth_client
.exchange_code(AuthorizationCode::new(auth_request.code))
.set_pkce_verifier(pkce_verifier)
.request_async(async_http_client)
.await
{
Ok(res) => res,
Err(e) => {
error!("could not exchange code: {e}");
return Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string()));
}
};
Ok(Redirect::temporary("/"))
}
错误如下:
--> src/main.rs:134:10
|
125 | let verifiers = state.verifiers.lock().unwrap();
| --------- has type `std::sync::MutexGuard<'_, std::collections::HashMap<std::string::String, std::string::String>>` which is not `Send`
134 | .await
| ^^^^^ await occurs here, with `verifiers` maybe used later
我知道互斥锁上的锁保持时间太长。我试图理解很多类似的问题,但我还无法理解这一点。
有人可以帮我解决这个可能非常微不足道的问题吗?
感谢您提前的帮助。
Rust 的
std::sync::Mutex
无法跨过 await
点,这就是您看到 type is not Send
编译错误的原因。您有两个选择:
在到达
await
点之前放下锁。您只需使用 verifiers
即可获得 pkce_verifier
,因此您可以内联它:
let pkce_verifier = state
.verifiers
.lock()
.unwrap()
.get(&auth_request.state)
.unwrap()
.into();
这有点难看,所以你也可以手动调用锁上的
drop()
:
let verifiers = state.verifiers.lock().unwrap();
let pkce_verifier = verifiers.get(&auth_request.state).unwrap().into();
drop(verifiers);
或者,您可以在内部块作用域内创建和使用锁,当块退出作用域时,它将自动删除锁:
let pkce_verifier = {
let verifiers = state.verifiers.lock().unwrap();
verifiers.get(&auth_request.state).unwrap().into()
};
您可以使用异步感知锁,例如
futures::lock::Mutex
。如果您认为将来需要跨 await
点使用互斥体,您可以切换到异步感知互斥体。否则,只要锁没有跨过 await
点,标准库互斥体就可以工作。
顺便说明一下,除非您确定该字段存在,否则在对象上调用
.unwrap()
是不好的做法。特别是在 HashMap
的情况下,您应该使用模式匹配或 Rust 提供的内置 Option
-to-Result
函数,例如 ok_or()
。