Refactor: Use common password validator
Refactors password validation logic to use a common `PasswordValidator` utility. This ensures consistent password validation rules across different parts of the application, including: - User login - Email sign-up - Password change - Account removal Also adds a string resource for password too long error message.
This commit is contained in:
@@ -30,6 +30,7 @@ import com.aiosman.ravenow.ui.comment.NoticeScreenHeader
|
|||||||
import com.aiosman.ravenow.ui.composables.ActionButton
|
import com.aiosman.ravenow.ui.composables.ActionButton
|
||||||
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
||||||
import com.aiosman.ravenow.ui.composables.TextInputField
|
import com.aiosman.ravenow.ui.composables.TextInputField
|
||||||
|
import com.aiosman.ravenow.utils.PasswordValidator
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,7 +59,7 @@ fun ChangePasswordScreen() {
|
|||||||
var currentPassword by remember { mutableStateOf("") }
|
var currentPassword by remember { mutableStateOf("") }
|
||||||
var newPassword by remember { mutableStateOf("") }
|
var newPassword by remember { mutableStateOf("") }
|
||||||
var confirmPassword by remember { mutableStateOf("") }
|
var confirmPassword by remember { mutableStateOf("") }
|
||||||
var errorMessage by remember { mutableStateOf("") }
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
var oldPasswordError by remember { mutableStateOf<String?>(null) }
|
var oldPasswordError by remember { mutableStateOf<String?>(null) }
|
||||||
@@ -66,17 +67,18 @@ fun ChangePasswordScreen() {
|
|||||||
var passwordError by remember { mutableStateOf<String?>(null) }
|
var passwordError by remember { mutableStateOf<String?>(null) }
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
fun validate(): Boolean {
|
fun validate(): Boolean {
|
||||||
oldPasswordError =
|
// 使用通用密码校验器校验当前密码
|
||||||
if (currentPassword.isEmpty()) "Please enter your current password" else null
|
val currentPasswordValidation = PasswordValidator.validateCurrentPassword(currentPassword, context)
|
||||||
passwordError = when {
|
oldPasswordError = if (!currentPasswordValidation.isValid) currentPasswordValidation.errorMessage else null
|
||||||
newPassword.length < 8 -> "Password must be at least 8 characters long"
|
|
||||||
!newPassword.any { it.isDigit() } -> "Password must contain at least one digit"
|
// 使用通用密码校验器校验新密码
|
||||||
!newPassword.any { it.isUpperCase() } -> "Password must contain at least one uppercase letter"
|
val newPasswordValidation = PasswordValidator.validatePassword(newPassword, context)
|
||||||
!newPassword.any { it.isLowerCase() } -> "Password must contain at least one lowercase letter"
|
passwordError = if (!newPasswordValidation.isValid) newPasswordValidation.errorMessage else null
|
||||||
else -> null
|
|
||||||
}
|
// 使用通用密码确认校验器
|
||||||
confirmPasswordError =
|
val confirmPasswordValidation = PasswordValidator.validatePasswordConfirmation(newPassword, confirmPassword, context)
|
||||||
if (newPassword != confirmPassword) "Passwords do not match" else null
|
confirmPasswordError = if (!confirmPasswordValidation.isValid) confirmPasswordValidation.errorMessage else null
|
||||||
|
|
||||||
return passwordError == null && confirmPasswordError == null && oldPasswordError == null
|
return passwordError == null && confirmPasswordError == null && oldPasswordError == null
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import com.aiosman.ravenow.ui.NavigationRoute
|
|||||||
import com.aiosman.ravenow.ui.comment.NoticeScreenHeader
|
import com.aiosman.ravenow.ui.comment.NoticeScreenHeader
|
||||||
import com.aiosman.ravenow.ui.composables.ActionButton
|
import com.aiosman.ravenow.ui.composables.ActionButton
|
||||||
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
||||||
|
import com.aiosman.ravenow.utils.PasswordValidator
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -58,8 +59,10 @@ fun RemoveAccountScreen() {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
fun removeAccount(password: String) {
|
fun removeAccount(password: String) {
|
||||||
if (password.isEmpty()) {
|
// 使用通用密码校验器
|
||||||
passwordError = "Please enter your correct password"
|
val passwordValidation = PasswordValidator.validateCurrentPassword(password, context)
|
||||||
|
if (!passwordValidation.isValid) {
|
||||||
|
passwordError = passwordValidation.errorMessage
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import com.aiosman.ravenow.ui.composables.CheckboxWithLabel
|
|||||||
import com.aiosman.ravenow.ui.composables.PolicyCheckbox
|
import com.aiosman.ravenow.ui.composables.PolicyCheckbox
|
||||||
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
||||||
import com.aiosman.ravenow.ui.composables.TextInputField
|
import com.aiosman.ravenow.ui.composables.TextInputField
|
||||||
|
import com.aiosman.ravenow.utils.PasswordValidator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -70,24 +71,13 @@ fun EmailSignupScreen() {
|
|||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
passwordError = when {
|
// 使用通用密码校验器
|
||||||
// 非空
|
val passwordValidation = PasswordValidator.validatePassword(password, context)
|
||||||
password.isEmpty() -> context.getString(R.string.text_error_password_required)
|
passwordError = if (!passwordValidation.isValid) passwordValidation.errorMessage else null
|
||||||
// 至少6位
|
|
||||||
password.length < 6 -> context.getString(R.string.text_error_password_format)
|
// 使用通用密码确认校验器
|
||||||
// 至少一个数字
|
val confirmPasswordValidation = PasswordValidator.validatePasswordConfirmation(password, confirmPassword, context)
|
||||||
!password.matches(Regex(".*\\d.*")) -> context.getString(R.string.text_error_password_format)
|
confirmPasswordError = if (!confirmPasswordValidation.isValid) confirmPasswordValidation.errorMessage else null
|
||||||
// 包含字母
|
|
||||||
!password.matches(Regex(".*[a-zA-Z].*")) -> context.getString(R.string.text_error_password_format)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
confirmPasswordError = when {
|
|
||||||
// 非空
|
|
||||||
confirmPassword.isEmpty() -> context.getString(R.string.text_error_confirm_password_required)
|
|
||||||
// 与密码一致
|
|
||||||
confirmPassword != password -> context.getString(R.string.text_error_confirm_password_mismatch)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
if (!acceptTerms) {
|
if (!acceptTerms) {
|
||||||
scope.launch(Dispatchers.Main) {
|
scope.launch(Dispatchers.Main) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
|||||||
import com.aiosman.ravenow.ui.composables.TextInputField
|
import com.aiosman.ravenow.ui.composables.TextInputField
|
||||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||||
import com.aiosman.ravenow.utils.GoogleLogin
|
import com.aiosman.ravenow.utils.GoogleLogin
|
||||||
|
import com.aiosman.ravenow.utils.PasswordValidator
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
@@ -71,8 +72,11 @@ fun UserAuthScreen() {
|
|||||||
fun validateForm(): Boolean {
|
fun validateForm(): Boolean {
|
||||||
emailError =
|
emailError =
|
||||||
if (email.isEmpty()) context.getString(R.string.text_error_email_required) else null
|
if (email.isEmpty()) context.getString(R.string.text_error_email_required) else null
|
||||||
passwordError =
|
|
||||||
if (password.isEmpty()) context.getString(R.string.text_error_password_required) else null
|
// 使用通用密码校验器
|
||||||
|
val passwordValidation = PasswordValidator.validateCurrentPassword(password, context)
|
||||||
|
passwordError = if (!passwordValidation.isValid) passwordValidation.errorMessage else null
|
||||||
|
|
||||||
return emailError == null && passwordError == null
|
return emailError == null && passwordError == null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
125
app/src/main/java/com/aiosman/ravenow/utils/PasswordValidator.kt
Normal file
125
app/src/main/java/com/aiosman/ravenow/utils/PasswordValidator.kt
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package com.aiosman.ravenow.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aiosman.ravenow.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码校验工具类
|
||||||
|
* 提供统一的密码校验规则和错误信息
|
||||||
|
*/
|
||||||
|
object PasswordValidator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码最大长度限制
|
||||||
|
*/
|
||||||
|
const val MAX_PASSWORD_LENGTH = 64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码最小长度限制
|
||||||
|
*/
|
||||||
|
const val MIN_PASSWORD_LENGTH = 6
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码校验结果
|
||||||
|
*/
|
||||||
|
data class ValidationResult(
|
||||||
|
val isValid: Boolean,
|
||||||
|
val errorMessage: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验密码格式
|
||||||
|
* @param password 密码
|
||||||
|
* @param context 上下文,用于获取本地化错误信息
|
||||||
|
* @return 校验结果
|
||||||
|
*/
|
||||||
|
fun validatePassword(password: String, context: Context): ValidationResult {
|
||||||
|
return when {
|
||||||
|
// 检查是否为空
|
||||||
|
password.isEmpty() -> ValidationResult(
|
||||||
|
false,
|
||||||
|
context.getString(R.string.text_error_password_required)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 检查长度不能超过64个字符
|
||||||
|
password.length > MAX_PASSWORD_LENGTH -> ValidationResult(
|
||||||
|
false,
|
||||||
|
context.getString(R.string.text_error_password_too_long, MAX_PASSWORD_LENGTH)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 检查最小长度
|
||||||
|
password.length < MIN_PASSWORD_LENGTH -> ValidationResult(
|
||||||
|
false,
|
||||||
|
context.getString(R.string.text_error_password_format)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 检查是否包含至少一个数字
|
||||||
|
!password.matches(Regex(".*\\d.*")) -> ValidationResult(
|
||||||
|
false,
|
||||||
|
context.getString(R.string.text_error_password_format)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 检查是否包含字母
|
||||||
|
!password.matches(Regex(".*[a-zA-Z].*")) -> ValidationResult(
|
||||||
|
false,
|
||||||
|
context.getString(R.string.text_error_password_format)
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> ValidationResult(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验密码确认
|
||||||
|
* @param password 原密码
|
||||||
|
* @param confirmPassword 确认密码
|
||||||
|
* @param context 上下文
|
||||||
|
* @return 校验结果
|
||||||
|
*/
|
||||||
|
fun validatePasswordConfirmation(
|
||||||
|
password: String,
|
||||||
|
confirmPassword: String,
|
||||||
|
context: Context
|
||||||
|
): ValidationResult {
|
||||||
|
return when {
|
||||||
|
confirmPassword.isEmpty() -> ValidationResult(
|
||||||
|
false,
|
||||||
|
context.getString(R.string.text_error_confirm_password_required)
|
||||||
|
)
|
||||||
|
|
||||||
|
confirmPassword.length > MAX_PASSWORD_LENGTH -> ValidationResult(
|
||||||
|
false,
|
||||||
|
context.getString(R.string.text_error_password_too_long, MAX_PASSWORD_LENGTH)
|
||||||
|
)
|
||||||
|
|
||||||
|
password != confirmPassword -> ValidationResult(
|
||||||
|
false,
|
||||||
|
context.getString(R.string.text_error_confirm_password_mismatch)
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> ValidationResult(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验当前密码(用于更改密码和删除账户场景)
|
||||||
|
* @param currentPassword 当前密码
|
||||||
|
* @param context 上下文
|
||||||
|
* @return 校验结果
|
||||||
|
*/
|
||||||
|
fun validateCurrentPassword(currentPassword: String, context: Context): ValidationResult {
|
||||||
|
return when {
|
||||||
|
currentPassword.isEmpty() -> ValidationResult(
|
||||||
|
false,
|
||||||
|
"Please enter your current password"
|
||||||
|
)
|
||||||
|
|
||||||
|
currentPassword.length > MAX_PASSWORD_LENGTH -> ValidationResult(
|
||||||
|
false,
|
||||||
|
context.getString(R.string.text_error_password_too_long, MAX_PASSWORD_LENGTH)
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> ValidationResult(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -185,5 +185,6 @@
|
|||||||
<string name="group_room_enter_fail">加入房间失败</string>
|
<string name="group_room_enter_fail">加入房间失败</string>
|
||||||
<string name="agent_createing">创建中...</string>
|
<string name="agent_createing">创建中...</string>
|
||||||
<string name="agent_find">发现</string>
|
<string name="agent_find">发现</string>
|
||||||
|
<string name="text_error_password_too_long">Password cannot exceed %1$d characters</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user