use crate::models::users::{user_sessions, users}; use crate::session::Session; use chrono::{Duration, Utc}; use sea_orm::{ActiveModelTrait, Set}; use uuid::Uuid; use crate::share::crypto; use crate::{AuthResult, AuthService}; /// 会话凭据摘要的算法标识,落在 `user_sessions.hash_key_id`。 pub const SESSION_HASH_KEY_ID: &str = "logical-session-v1"; /// 会话 ID 的摘要。会话 ID 本身即凭据,库里只存摘要。 pub fn session_token_hash(id: Uuid) -> String { crypto::sha256_hex(id.to_string().as_bytes()) } impl AuthService { #[tracing::instrument(skip_all, fields(user_id = user.id, mfa = mfa, request_id = %envelope::context::resolve_request_id(request_id)))] pub async fn bind_auth_session( &self, session: &Session, user: &users::Model, mfa: bool, request_id: Option<&str>, ) -> AuthResult<()> { let now = Utc::now(); let id = Uuid::now_v7(); let ttl = i64::try_from(self.session_max_age_secs()) .unwrap_or(86400) .max(1); user_sessions::ActiveModel { id: Set(id), user_id: Set(user.id), token_hash: Set(session_token_hash(id)), hash_key_id: Set(SESSION_HASH_KEY_ID.into()), security_version: Set(user.security_version), expires_at: Set(now + Duration::seconds(ttl)), mfa_verified_at: Set(mfa.then_some(now)), ip_address: Set(session.client_ip()?), user_agent: Set(session.user_agent()?), ..Default::default() } .insert(self.db().write()) .await?; session.set("auth_session_id", id.to_string())?; session.set("mfa_verified", mfa)?; session.set_user_id(user.id)?; session.set_security_version(user.security_version)?; session.renew(); Ok(()) } }