feat: improve registration validation

This commit is contained in:
Tim
2025-07-04 22:15:05 +08:00
parent b17df96060
commit 968341dcb1
5 changed files with 79 additions and 11 deletions

View File

@@ -3,12 +3,19 @@ package com.openisle.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.openisle.exception.FieldException;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(FieldException.class)
public ResponseEntity<?> handleFieldException(FieldException ex) {
return ResponseEntity.badRequest()
.body(Map.of("error", ex.getMessage(), "field", ex.getField()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleException(Exception ex) {
return ResponseEntity.badRequest().body(Map.of("error", ex.getMessage()));

View File

@@ -0,0 +1,19 @@
package com.openisle.exception;
import lombok.Getter;
/**
* Exception carrying a target field name. Useful for reporting
* validation errors to clients so they can display feedback near
* the appropriate input element.
*/
@Getter
public class FieldException extends RuntimeException {
private final String field;
public FieldException(String field, String message) {
super(message);
this.field = field;
}
}

View File

@@ -1,6 +1,7 @@
package com.openisle.service;
import com.openisle.model.PasswordStrength;
import com.openisle.exception.FieldException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -14,7 +15,7 @@ public class PasswordValidator {
public void validate(String password) {
if (password == null || password.isEmpty()) {
throw new IllegalArgumentException("Password cannot be empty");
throw new FieldException("password", "Password cannot be empty");
}
switch (strength) {
case MEDIUM:
@@ -31,34 +32,34 @@ public class PasswordValidator {
private void checkLow(String password) {
if (password.length() < 6) {
throw new IllegalArgumentException("Password must be at least 6 characters long");
throw new FieldException("password", "Password must be at least 6 characters long");
}
}
private void checkMedium(String password) {
if (password.length() < 8) {
throw new IllegalArgumentException("Password must be at least 8 characters long");
throw new FieldException("password", "Password must be at least 8 characters long");
}
if (!password.matches(".*[A-Za-z].*") || !password.matches(".*\\d.*")) {
throw new IllegalArgumentException("Password must contain letters and numbers");
throw new FieldException("password", "Password must contain letters and numbers");
}
}
private void checkHigh(String password) {
if (password.length() < 12) {
throw new IllegalArgumentException("Password must be at least 12 characters long");
throw new FieldException("password", "Password must be at least 12 characters long");
}
if (!password.matches(".*[A-Z].*")) {
throw new IllegalArgumentException("Password must contain uppercase letters");
throw new FieldException("password", "Password must contain uppercase letters");
}
if (!password.matches(".*[a-z].*")) {
throw new IllegalArgumentException("Password must contain lowercase letters");
throw new FieldException("password", "Password must contain lowercase letters");
}
if (!password.matches(".*\\d.*")) {
throw new IllegalArgumentException("Password must contain numbers");
throw new FieldException("password", "Password must contain numbers");
}
if (!password.matches(".*[^A-Za-z0-9].*")) {
throw new IllegalArgumentException("Password must contain special characters");
throw new FieldException("password", "Password must contain special characters");
}
}
}

View File

@@ -3,6 +3,7 @@ package com.openisle.service;
import com.openisle.model.User;
import com.openisle.model.Role;
import com.openisle.service.PasswordValidator;
import com.openisle.exception.FieldException;
import com.openisle.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@@ -26,7 +27,7 @@ public class UserService {
if (byUsername.isPresent()) {
User u = byUsername.get();
if (u.isVerified()) { // 已验证 → 直接拒绝
throw new IllegalStateException("User name already exists");
throw new FieldException("username", "User name already exists");
}
// 未验证 → 允许“重注册”:覆盖必要字段并重新发验证码
u.setEmail(email); // 若不允许改邮箱可去掉
@@ -40,7 +41,7 @@ public class UserService {
if (byEmail.isPresent()) {
User u = byEmail.get();
if (u.isVerified()) { // 已验证 → 直接拒绝
throw new IllegalStateException("User email already exists");
throw new FieldException("email", "User email already exists");
}
// 未验证 → 允许“重注册”
u.setUsername(username); // 若不允许改用户名可去掉