实现axum-login的AuthnBackend trait时遭遇生命周期不匹配错误
嗨,我刚碰到过类似的问题,来帮你理一理!你遇到的生命周期错误,核心原因是axum-login的AuthnBackend trait里的方法,和你用async fn实现时生成的Future生命周期没有对齐。具体来说,问题主要出在get_user方法的参数处理上,另外我们也可以顺便优化一些细节避免其他潜在问题。
问题拆解
Rust的async fn会被自动展开成返回一个Future,这个Future会捕获方法里的引用(比如&self和&user_id)。而axum-login的trait对这个Future的生命周期有隐含要求,当你直接使用&UserId<Self>(也就是&u64)时,Future会持有这个引用的生命周期,导致和trait定义的约束不匹配。
解决步骤
1. 修正get_user方法的参数处理
因为u64是Copy类型,我们可以直接解引用user_id拿到它的所有权,这样Future就不需要再持有这个引用的生命周期了,代码修改如下:
async fn get_user(&self, user_id: &UserId<Self>) -> Result<Option<Self::User>, Self::Error> { // 解引用拿到所有权,消除引用的生命周期绑定 let user_id = *user_id; // Connect to db let conn = self.db_con.lock().await; // Load user data from db let user_data = match conn.query_row( "SELECT data FROM users WHERE id = ?1", [user_id], // 这里直接用值,不用引用 |row| row.get::<usize, Vec<u8>>(0), ) { Ok(data) => data, Err(err) => match err { rusqlite::Error::QueryReturnedNoRows => { return Ok(None); } _ => return Err(self::UserBackendError::Rusqlite(err)), }, }; // 这里把unwrap改成?,并在错误枚举里添加Serde错误变体 let user: User = from_slice(&user_data)?; Ok(Some(user)) }
2. 修复authenticate方法的反序列化错误处理
你现在用了from_slice(&user_data).unwrap();,这会在反序列化失败时直接panic,非常不健壮。我们需要把它改成错误传播,首先给UserBackendError添加Serde相关的错误变体:
#[derive(Debug, thiserror::Error)] pub enum UserBackendError { #[error(transparent)] Rusqlite(#[from] rusqlite::Error), #[error(transparent)] PasswordVerify(#[from] password_auth::VerifyError), #[error(transparent)] SerdeCbor(#[from] serde_cbor::Error), // 新增这个变体支持Serde错误传播 }
然后把authenticate里的反序列化代码改成:
let user: User = from_slice(&user_data)?;
3. 确认authenticate方法的生命周期对齐
其实authenticate的错误也是因为类似的隐含生命周期问题,不过当我们确保所有操作都不持有不必要的引用(比如你的代码里creds是所有权传递,本身没问题),再加上上面的修改后,这个错误也会跟着消失。
为什么SQLx的例子没这个问题?
SQLx的query_row是原生异步方法,而且它的连接池设计和rusqlite的同步连接+Mutex模式不同,SQLx的异步API不会引入额外的生命周期绑定,所以没暴露这个问题。而rusqlite是同步库,我们用tokio::sync::Mutex包裹后,需要更小心处理引用的生命周期。
这样修改后,编译器的生命周期错误应该就会消失了,代码也会更健壮~
内容来源于stack exchange

