You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MathLearningApp/src/main/java/mathlearning/service/UserService.java

227 lines
7.5 KiB

package mathlearning.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import mathlearning.model.User;
import at.favre.lib.crypto.bcrypt.BCrypt;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class UserService {
private static final String USERS_FILE = "data/users.json";
private static final long VERIFICATION_CODE_EXPIRY_MINUTES = 10;
private Map<String, User> users;
private Map<String, LocalDateTime> verificationCodeTimestamps;
private ObjectMapper objectMapper;
public UserService() {
this.users = new ConcurrentHashMap<>();
this.verificationCodeTimestamps = new ConcurrentHashMap<>();
this.objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
loadUsers();
}
private void loadUsers() {
try {
File file = new File(USERS_FILE);
if (file.exists() && file.length() > 0) {
User[] userArray = objectMapper.readValue(file, User[].class);
for (User user : userArray) {
users.put(user.getEmail(), user);
}
System.out.println("成功加载 " + userArray.length + " 个用户");
} else {
saveUsers();
System.out.println("创建新的用户数据文件");
}
} catch (IOException e) {
System.err.println("加载用户数据失败: " + e.getMessage());
backupAndRecreateFile();
}
}
private void backupAndRecreateFile() {
try {
File file = new File(USERS_FILE);
if (file.exists()) {
File backup = new File(USERS_FILE + ".bak." + System.currentTimeMillis());
file.renameTo(backup);
System.err.println("损坏的文件已备份为: " + backup.getName());
}
saveUsers();
System.out.println("已重新创建用户数据文件");
} catch (Exception e) {
System.err.println("备份文件失败: " + e.getMessage());
}
}
private void saveUsers() {
try {
File file = new File(USERS_FILE);
file.getParentFile().mkdirs();
objectMapper.writerWithDefaultPrettyPrinter().writeValue(file, users.values().toArray(new User[0]));
} catch (IOException e) {
System.err.println("保存用户数据失败: " + e.getMessage());
e.printStackTrace();
}
}
public boolean registerUser(String email, String verificationCode) {
if (users.containsKey(email)) {
return false; // 用户已存在
}
// 生成密码占位符,实际密码在验证后设置
String tempPasswordHash = BCrypt.withDefaults().hashToString(12, "temp".toCharArray());
User user = new User(email, tempPasswordHash, verificationCode);
users.put(email, user);
verificationCodeTimestamps.put(email, LocalDateTime.now());
saveUsers();
return true;
}
public boolean verifyUser(String username, String email, String verificationCode, String password) {
User user = users.get(email);
if (user == null || !user.getVerificationCode().equals(verificationCode)) {
return false;
}
// 检查验证码是否过期
LocalDateTime codeTime = verificationCodeTimestamps.get(email);
if (codeTime == null || codeTime.plusMinutes(VERIFICATION_CODE_EXPIRY_MINUTES).isBefore(LocalDateTime.now())) {
return false;
}
// 验证密码格式
if (!validatePassword(password)) {
return false;
}
// 设置实际密码
String passwordHash = BCrypt.withDefaults().hashToString(12, password.toCharArray());
user.setPasswordHash(passwordHash);
user.setUsername(username);
user.setVerified(true);
verificationCodeTimestamps.remove(email);
saveUsers();
return true;
}
public boolean login(String email, String password) {
User user = users.get(email);
if (user == null || !user.isVerified()) {
return false;
}
BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), user.getPasswordHash());
return result.verified;
}
public boolean changePassword(String email, String oldPassword, String newPassword) {
User user = users.get(email);
if (user == null) {
return false;
}
// 验证旧密码
BCrypt.Result result = BCrypt.verifyer().verify(oldPassword.toCharArray(), user.getPasswordHash());
if (!result.verified) {
return false;
}
// 验证新密码格式
if (!validatePassword(newPassword)) {
return false;
}
// 更新密码
String newPasswordHash = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray());
user.setPasswordHash(newPasswordHash);
saveUsers();
return true;
}
public boolean setPasswordResetCode(String email, String verificationCode) {
User user = users.get(email);
if (user == null) {
return false; // 用户不存在
}
user.setVerificationCode(verificationCode);
verificationCodeTimestamps.put(email, LocalDateTime.now());
saveUsers();
return true;
}
public boolean resetPasswordWithCode(String email, String verificationCode, String newPassword) {
User user = users.get(email);
if (user == null) {
return false;
}
// 验证验证码
if (!user.getVerificationCode().equals(verificationCode)) {
return false;
}
// 检查验证码是否过期
LocalDateTime codeTime = verificationCodeTimestamps.get(email);
if (codeTime == null || codeTime.plusMinutes(VERIFICATION_CODE_EXPIRY_MINUTES).isBefore(LocalDateTime.now())) {
return false;
}
// 验证新密码格式
if (!validatePassword(newPassword)) {
return false;
}
// 更新密码
String newPasswordHash = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray());
user.setPasswordHash(newPasswordHash);
verificationCodeTimestamps.remove(email); // 清除验证码
saveUsers();
return true;
}
public boolean validatePassword(String password) {
if (password.length() < 6 || password.length() > 10) {
return false;
}
boolean hasUpper = false;
boolean hasLower = false;
boolean hasDigit = false;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) hasUpper = true;
if (Character.isLowerCase(c)) hasLower = true;
if (Character.isDigit(c)) hasDigit = true;
}
return hasUpper && hasLower && hasDigit;
}
public boolean userExists(String email) {
return users.containsKey(email);
}
public boolean isUserExistsAndVerified(String email) {
User user = users.get(email);
return user != null && user.isVerified();
}
public boolean isValidEmail(String email) {
return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}
public User getUser(String email ) {
User user = users.get(email);
return user;
}
}