|
|
|
@ -0,0 +1,263 @@
|
|
|
|
|
import java.security.*;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.regex.*;
|
|
|
|
|
import javax.crypto.*;
|
|
|
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用户管理服务类
|
|
|
|
|
* 实现功能:用户注册/登录、角色权限管理、密码安全、会话管理
|
|
|
|
|
*/
|
|
|
|
|
class UserManagementService {
|
|
|
|
|
// 内存存储结构(实际项目应使用数据库)
|
|
|
|
|
private final Map<String, User> userDatabase = new HashMap<>();
|
|
|
|
|
private final Map<String, Session> activeSessions = new HashMap<>();
|
|
|
|
|
private static final String SECRET_KEY = "express@Sys#Key2025"; // 加密密钥(生产环境应存储在安全配置)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用户注册
|
|
|
|
|
* @param username 用户名(4-16位字母数字)
|
|
|
|
|
* @param password 明文密码(需符合复杂度要求)
|
|
|
|
|
* @param role 用户角色(admin/staff/customer)
|
|
|
|
|
* @return 注册结果
|
|
|
|
|
*/
|
|
|
|
|
public RegistrationResult registerUser(String username, String password, String role)
|
|
|
|
|
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
|
|
|
|
|
|
|
|
// 1. 参数验证
|
|
|
|
|
if (!isValidUsername(username)) {
|
|
|
|
|
return new RegistrationResult(false, "用户名需为4-16位字母数字");
|
|
|
|
|
}
|
|
|
|
|
if (userDatabase.containsKey(username)) {
|
|
|
|
|
return new RegistrationResult(false, "用户名已存在");
|
|
|
|
|
}
|
|
|
|
|
if (!isValidPassword(password)) {
|
|
|
|
|
return new RegistrationResult(false, "密码需包含大小写字母和数字,至少8位");
|
|
|
|
|
}
|
|
|
|
|
if (!isValidRole(role)) {
|
|
|
|
|
return new RegistrationResult(false, "无效角色类型");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 密码加密处理
|
|
|
|
|
String salt = generateSalt();
|
|
|
|
|
String hashedPwd = hashPassword(password, salt);
|
|
|
|
|
|
|
|
|
|
// 3. 创建用户对象
|
|
|
|
|
User newUser = new User(username, hashedPwd, salt, role);
|
|
|
|
|
userDatabase.put(username, newUser);
|
|
|
|
|
return new RegistrationResult(true, "注册成功", newUser);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用户登录认证
|
|
|
|
|
* @param username 用户名
|
|
|
|
|
* @param password 明文密码
|
|
|
|
|
* @return 认证结果(含会话令牌)
|
|
|
|
|
*/
|
|
|
|
|
public AuthenticationResult authenticate(String username, String password)
|
|
|
|
|
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
|
|
|
|
|
|
|
|
User user = userDatabase.get(username);
|
|
|
|
|
if (user == null) {
|
|
|
|
|
return new AuthenticationResult(false, "用户不存在");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 验证密码
|
|
|
|
|
String testHash = hashPassword(password, user.getSalt());
|
|
|
|
|
if (!testHash.equals(user.getHashedPassword())) {
|
|
|
|
|
return new AuthenticationResult(false, "密码错误");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成会话令牌(JWT风格)
|
|
|
|
|
String token = generateSessionToken(username);
|
|
|
|
|
activeSessions.put(token, new Session(token, username, new Date()));
|
|
|
|
|
|
|
|
|
|
return new AuthenticationResult(true, "认证成功", token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 密码复杂度验证
|
|
|
|
|
* @param password 待验证密码
|
|
|
|
|
* @return 是否符合复杂度要求
|
|
|
|
|
*/
|
|
|
|
|
private boolean isValidPassword(String password) {
|
|
|
|
|
if (password.length() < 8) return false;
|
|
|
|
|
|
|
|
|
|
boolean hasUpper = false, hasLower = false, hasDigit = false;
|
|
|
|
|
for (char c : password.toCharArray()) {
|
|
|
|
|
if (Character.isUpperCase(c)) hasUpper = true;
|
|
|
|
|
else if (Character.isLowerCase(c)) hasLower = true;
|
|
|
|
|
else if (Character.isDigit(c)) hasDigit = true;
|
|
|
|
|
}
|
|
|
|
|
return hasUpper && hasLower && hasDigit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成加密盐值(16字节随机数)
|
|
|
|
|
*/
|
|
|
|
|
private String generateSalt() {
|
|
|
|
|
SecureRandom random = new SecureRandom();
|
|
|
|
|
byte[] salt = new byte[16];
|
|
|
|
|
random.nextBytes(salt);
|
|
|
|
|
return Base64.getEncoder().encodeToString(salt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* PBKDF2密码哈希算法
|
|
|
|
|
*/
|
|
|
|
|
private String hashPassword(String password, String salt)
|
|
|
|
|
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
|
|
|
|
|
|
|
|
int iterations = 10000;
|
|
|
|
|
int keyLength = 256;
|
|
|
|
|
char[] passwordChars = password.toCharArray();
|
|
|
|
|
byte[] saltBytes = Base64.getDecoder().decode(salt);
|
|
|
|
|
|
|
|
|
|
PBEKeySpec spec = new PBEKeySpec(passwordChars, saltBytes, iterations, keyLength);
|
|
|
|
|
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
|
|
|
|
byte[] hash = skf.generateSecret(spec).getEncoded();
|
|
|
|
|
|
|
|
|
|
return String.format("%d$%s$%s",
|
|
|
|
|
iterations,
|
|
|
|
|
Base64.getEncoder().encodeToString(saltBytes),
|
|
|
|
|
Base64.getEncoder().encodeToString(hash));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 会话令牌生成(HMAC-SHA256)
|
|
|
|
|
*/
|
|
|
|
|
private String generateSessionToken(String username) {
|
|
|
|
|
try {
|
|
|
|
|
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
|
|
|
|
|
SecretKeySpec secret_key = new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA256");
|
|
|
|
|
sha256_HMAC.init(secret_key);
|
|
|
|
|
|
|
|
|
|
String baseString = username + new Date().getTime();
|
|
|
|
|
return Base64.getEncoder().encodeToString(
|
|
|
|
|
sha256_HMAC.doFinal(baseString.getBytes())
|
|
|
|
|
);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new RuntimeException("令牌生成失败", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用户角色验证装饰器
|
|
|
|
|
* @param requiredRole 所需权限等级
|
|
|
|
|
*/
|
|
|
|
|
public static class RoleValidator {
|
|
|
|
|
private final String currentRole;
|
|
|
|
|
|
|
|
|
|
public RoleValidator(String sessionToken) {
|
|
|
|
|
// 实际应从会话中获取用户角色
|
|
|
|
|
this.currentRole = "customer"; // 示例值
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void validate(String requiredRole) throws InsufficientPermissionsException {
|
|
|
|
|
if (!currentRole.equals(requiredRole)) {
|
|
|
|
|
throw new InsufficientPermissionsException(
|
|
|
|
|
String.format("需要%s权限,当前角色:%s", requiredRole, currentRole));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用户实体类
|
|
|
|
|
*/
|
|
|
|
|
static class User {
|
|
|
|
|
private final String username;
|
|
|
|
|
private final String hashedPassword;
|
|
|
|
|
private final String salt;
|
|
|
|
|
private final String role;
|
|
|
|
|
|
|
|
|
|
User(String username, String hashedPassword, String salt, String role) {
|
|
|
|
|
this.username = username;
|
|
|
|
|
this.hashedPassword = hashedPassword;
|
|
|
|
|
this.salt = salt;
|
|
|
|
|
this.role = role;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Getter方法(根据安全需求限制访问)
|
|
|
|
|
public String getUsername() { return username; }
|
|
|
|
|
public String getHashedPassword() { return hashedPassword; }
|
|
|
|
|
public String getSalt() { return salt; }
|
|
|
|
|
public String getRole() { return role; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 会话管理类
|
|
|
|
|
*/
|
|
|
|
|
static class Session {
|
|
|
|
|
private final String token;
|
|
|
|
|
private final String username;
|
|
|
|
|
private final Date createdAt;
|
|
|
|
|
|
|
|
|
|
Session(String token, String username, Date createdAt) {
|
|
|
|
|
this.token = token;
|
|
|
|
|
this.username = username;
|
|
|
|
|
this.createdAt = createdAt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Getter方法
|
|
|
|
|
public String getToken() { return token; }
|
|
|
|
|
public String getUsername() { return username; }
|
|
|
|
|
public Date getCreatedAt() { return createdAt; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 注册结果封装类
|
|
|
|
|
*/
|
|
|
|
|
static class RegistrationResult {
|
|
|
|
|
private final boolean success;
|
|
|
|
|
private final String message;
|
|
|
|
|
private final User user;
|
|
|
|
|
|
|
|
|
|
RegistrationResult(boolean success, String message) {
|
|
|
|
|
this(success, message, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RegistrationResult(boolean success, String message, User user) {
|
|
|
|
|
this.success = success;
|
|
|
|
|
this.message = message;
|
|
|
|
|
this.user = user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Getter方法
|
|
|
|
|
public boolean isSuccess() { return success; }
|
|
|
|
|
public String getMessage() { return message; }
|
|
|
|
|
public User getUser() { return user; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 认证结果封装类
|
|
|
|
|
*/
|
|
|
|
|
static class AuthenticationResult {
|
|
|
|
|
private final boolean success;
|
|
|
|
|
private final String message;
|
|
|
|
|
private final String token;
|
|
|
|
|
|
|
|
|
|
AuthenticationResult(boolean success, String message) {
|
|
|
|
|
this(success, message, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AuthenticationResult(boolean success, String message, String token) {
|
|
|
|
|
this.success = success;
|
|
|
|
|
this.message = message;
|
|
|
|
|
this.token = token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Getter方法
|
|
|
|
|
public boolean isSuccess() { return success; }
|
|
|
|
|
public String getMessage() { return message; }
|
|
|
|
|
public String getToken() { return token; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 权限不足异常
|
|
|
|
|
*/
|
|
|
|
|
static class InsufficientPermissionsException extends SecurityException {
|
|
|
|
|
public InsufficientPermissionsException(String message) {
|
|
|
|
|
super(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|