This commit is contained in:
2026-04-24 16:15:00 +08:00
parent f65177b147
commit b355568189
16 changed files with 495 additions and 217 deletions

View File

@@ -65,7 +65,7 @@ pub async fn password_entry(
fn map_password_entry_error(error: PasswordEntryError) -> AppError {
match error {
PasswordEntryError::InvalidUsername => AppError::from_status(StatusCode::BAD_REQUEST)
.with_message("用户名只允许 3 到 24 位字母、数字、下划线")
.with_message("手机号格式不正确")
.with_details(json!({
"field": "username",
})),
@@ -80,10 +80,10 @@ fn map_password_entry_error(error: PasswordEntryError) -> AppError {
"field": "username",
})),
PasswordEntryError::InvalidCredentials => {
AppError::from_status(StatusCode::UNAUTHORIZED).with_message("用户名或密码错误")
AppError::from_status(StatusCode::UNAUTHORIZED).with_message("手机号或密码错误")
}
PasswordEntryError::UserNotFound => {
AppError::from_status(StatusCode::UNAUTHORIZED).with_message("用户名或密码错误")
AppError::from_status(StatusCode::UNAUTHORIZED).with_message("手机号或密码错误")
}
PasswordEntryError::Store(_) | PasswordEntryError::PasswordHash(_) => {
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(error.to_string())

View File

@@ -475,24 +475,25 @@ impl PasswordEntryService {
&self,
input: PasswordEntryInput,
) -> Result<PasswordEntryResult, PasswordEntryError> {
let username = normalize_username(&input.username)?;
validate_password(&input.password)?;
if let Some(existing_user) = self.store.find_by_username(&username)? {
if !existing_user.password_login_enabled {
// 登录面板现在固定使用手机号作为密码登录标识;先走手机号索引,
// 再保留历史用户名路径给开发游客和旧测试数据使用。
if let Ok(normalized_phone) = normalize_mainland_china_phone_number(&input.username) {
let Some(existing_user) = self
.store
.find_by_phone_number_for_password(&normalized_phone.e164)?
else {
return Err(PasswordEntryError::InvalidCredentials);
}
let is_valid = verify_password(&existing_user.password_hash, &input.password)
.await
.map_err(|error| PasswordEntryError::PasswordHash(error.to_string()))?;
if !is_valid {
return Err(PasswordEntryError::InvalidCredentials);
}
};
return Ok(PasswordEntryResult {
user: existing_user.user,
created: false,
});
return verify_stored_password_user(existing_user, &input.password).await;
}
let username = normalize_username(&input.username)?;
if let Some(existing_user) = self.store.find_by_username(&username)? {
return verify_stored_password_user(existing_user, &input.password).await;
}
Err(PasswordEntryError::InvalidCredentials)
@@ -1292,6 +1293,24 @@ impl InMemoryAuthStore {
.cloned())
}
fn find_by_phone_number_for_password(
&self,
phone_number: &str,
) -> Result<Option<StoredPasswordUser>, PasswordEntryError> {
let state = self
.inner
.lock()
.map_err(|_| PasswordEntryError::Store("用户仓储锁已中毒".to_string()))?;
let Some(user_id) = state.phone_to_user_id.get(phone_number) else {
return Ok(None);
};
Ok(state
.users_by_username
.values()
.find(|stored_user| stored_user.user.id == *user_id)
.cloned())
}
fn create_phone_user(
&self,
phone_number: PhoneNumberSnapshot,
@@ -2220,6 +2239,27 @@ fn validate_password(password: &str) -> Result<(), PasswordEntryError> {
Ok(())
}
async fn verify_stored_password_user(
existing_user: StoredPasswordUser,
password: &str,
) -> Result<PasswordEntryResult, PasswordEntryError> {
if !existing_user.password_login_enabled {
return Err(PasswordEntryError::InvalidCredentials);
}
let is_valid = verify_password(&existing_user.password_hash, password)
.await
.map_err(|error| PasswordEntryError::PasswordHash(error.to_string()))?;
if !is_valid {
return Err(PasswordEntryError::InvalidCredentials);
}
Ok(PasswordEntryResult {
user: existing_user.user,
created: false,
})
}
fn verify_sms_code_format(verify_code: &str) -> Result<(), PhoneAuthError> {
let verify_code = verify_code.trim();
if verify_code.len() != SMS_CODE_LENGTH