登录增加验证码切换

This commit is contained in:
2026-04-06 22:58:55 +08:00
parent b0751cf45e
commit dd1bda704f
4 changed files with 111 additions and 8 deletions

View File

@@ -20,7 +20,7 @@ public class AuthController {
private UserService userService; private UserService userService;
@PostMapping("/login") @PostMapping("/login")
public ResponseEntity<?> login(@RequestBody Map<String, String> request, HttpSession session) { public ResponseEntity<?> login(@RequestBody Map<String, String> request, HttpSession session, jakarta.servlet.http.HttpServletRequest httpRequest) {
String username = request.get("username"); String username = request.get("username");
String password = request.get("password"); String password = request.get("password");
String captcha = request.get("captcha"); String captcha = request.get("captcha");
@@ -31,7 +31,9 @@ public class AuthController {
} }
try { try {
String token = userService.login(username, password); // 获取客户端 IP
String clientIp = getClientIp(httpRequest);
String token = userService.login(username, password, clientIp);
User user = userService.findByUsername(username); User user = userService.findByUsername(username);
// 直接读取存储空间,不重算 // 直接读取存储空间,不重算
@@ -64,7 +66,7 @@ public class AuthController {
} }
@PostMapping("/register") @PostMapping("/register")
public ResponseEntity<?> register(@RequestBody Map<String, String> request) { public ResponseEntity<?> register(@RequestBody Map<String, String> request, jakarta.servlet.http.HttpServletRequest httpRequest) {
String username = request.get("username"); String username = request.get("username");
String password = request.get("password"); String password = request.get("password");
String nickname = request.get("nickname"); String nickname = request.get("nickname");
@@ -73,7 +75,9 @@ public class AuthController {
return ResponseEntity.badRequest().body(Map.of("message", "用户名已存在")); return ResponseEntity.badRequest().body(Map.of("message", "用户名已存在"));
} }
User user = userService.createUser(username, password, nickname != null ? nickname : username); // 获取客户端 IP
String clientIp = getClientIp(httpRequest);
User user = userService.createUser(username, password, nickname != null ? nickname : username, clientIp);
return ResponseEntity.ok(Map.of("message", "注册成功", "data", Map.of("id", user.getId()))); return ResponseEntity.ok(Map.of("message", "注册成功", "data", Map.of("id", user.getId())));
} }
@@ -115,4 +119,22 @@ public class AuthController {
return ResponseEntity.ok(body); return ResponseEntity.ok(body);
} }
/**
* 获取客户端真实 IP支持代理
*/
private String getClientIp(jakarta.servlet.http.HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 多级代理时取第一个
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
} }

View File

@@ -30,4 +30,10 @@ public class User extends BaseEntity {
@TableField("storage_limit") @TableField("storage_limit")
private Long storageLimit; private Long storageLimit;
@TableField("register_ip")
private String registerIp;
@TableField("allowed_ips")
private String allowedIps; // 允许的IP段逗号分隔如 "192.168.1.0/24,10.0.0.0/8"
} }

View File

@@ -41,7 +41,7 @@ public class UserService {
return userMapper.selectById(id); return userMapper.selectById(id);
} }
public String login(String username, String password) { public String login(String username, String password, String clientIp) {
User user = findByUsername(username); User user = findByUsername(username);
if (user == null) { if (user == null) {
throw new RuntimeException("用户不存在"); throw new RuntimeException("用户不存在");
@@ -49,14 +49,86 @@ public class UserService {
if (!passwordEncoder.matches(password, user.getPassword())) { if (!passwordEncoder.matches(password, user.getPassword())) {
throw new RuntimeException("密码错误"); throw new RuntimeException("密码错误");
} }
// 校验 IP检查客户端 IP 是否在允许范围内
String allowedIps = user.getAllowedIps();
if (allowedIps != null && !allowedIps.isEmpty()) {
if (!isIpAllowed(clientIp, allowedIps)) {
throw new RuntimeException("登录IP不在允许范围内");
}
}
return jwtUtil.generateToken(username, user.getId()); return jwtUtil.generateToken(username, user.getId());
} }
/**
* 检查客户端IP是否在允许的IP段列表中
*/
private boolean isIpAllowed(String clientIp, String allowedIps) {
if (clientIp == null || clientIp.isEmpty()) {
return false;
}
String[] ips = allowedIps.split(",");
for (String ip : ips) {
ip = ip.trim();
if (ip.isEmpty()) continue;
// 精确匹配
if (clientIp.equals(ip)) {
return true;
}
// 网段匹配(如 192.168.1.0/24
if (ip.contains("/")) {
if (cidrContains(ip, clientIp)) {
return true;
}
}
}
return false;
}
/**
* 判断 CIDR 网段是否包含指定 IP
*/
private boolean cidrContains(String cidr, String ip) {
try {
String[] parts = cidr.split("/");
String baseIp = parts[0];
int prefix = Integer.parseInt(parts[1]);
byte[] ipBytes = ipToBytes(ip);
byte[] baseBytes = ipToBytes(baseIp);
int mask = 0xFFFFFFFF << (32 - prefix);
int ipInt = bytesToInt(ipBytes);
int baseInt = bytesToInt(baseBytes);
return (ipInt & mask) == (baseInt & mask);
} catch (Exception e) {
return false;
}
}
private byte[] ipToBytes(String ip) {
String[] parts = ip.split("\\.");
byte[] bytes = new byte[4];
for (int i = 0; i < 4; i++) {
bytes[i] = (byte) Integer.parseInt(parts[i]);
}
return bytes;
}
private int bytesToInt(byte[] bytes) {
return ((bytes[0] & 0xFF) << 24) | ((bytes[1] & 0xFF) << 16) |
((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);
}
public void logout(Long userId) { public void logout(Long userId) {
jwtUtil.invalidateToken(userId); jwtUtil.invalidateToken(userId);
} }
public User createUser(String username, String password, String nickname) { public User createUser(String username, String password, String nickname, String registerIp) {
User user = new User(); User user = new User();
user.setUsername(username); user.setUsername(username);
user.setPassword(passwordEncoder.encode(password)); user.setPassword(passwordEncoder.encode(password));
@@ -64,6 +136,7 @@ public class UserService {
user.setStatus(1); user.setStatus(1);
user.setStorageUsed(0L); user.setStorageUsed(0L);
user.setStorageLimit((long) storageLimitGb * 1024 * 1024 * 1024); user.setStorageLimit((long) storageLimitGb * 1024 * 1024 * 1024);
user.setRegisterIp(registerIp);
userMapper.insert(user); userMapper.insert(user);
return user; return user;
} }

View File

@@ -16,6 +16,8 @@ CREATE TABLE IF NOT EXISTS sys_user (
status INT DEFAULT 1 COMMENT '状态 0-禁用 1-启用', status INT DEFAULT 1 COMMENT '状态 0-禁用 1-启用',
storage_used BIGINT DEFAULT 0 COMMENT '已用存储空间(字节)', storage_used BIGINT DEFAULT 0 COMMENT '已用存储空间(字节)',
storage_limit BIGINT DEFAULT 10737418240 COMMENT '存储限制(字节) 默认10GB', storage_limit BIGINT DEFAULT 10737418240 COMMENT '存储限制(字节) 默认10GB',
register_ip VARCHAR(50) COMMENT '注册IP',
allowed_ips VARCHAR(500) COMMENT '允许的IP段逗号分隔',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
is_deleted INT DEFAULT 0 COMMENT '是否删除 0-否 1-是' is_deleted INT DEFAULT 0 COMMENT '是否删除 0-否 1-是'
@@ -73,6 +75,6 @@ CREATE TABLE IF NOT EXISTS sys_message (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息表';
-- 插入默认管理员账户 (密码: admin123) -- 插入默认管理员账户 (密码: admin123)
INSERT INTO sys_user (username, password, nickname, status, storage_limit) INSERT INTO sys_user (username, password, nickname, status, storage_limit, allowed_ips)
VALUES ('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', '管理员', 1, 10737418240) VALUES ('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', '管理员', 1, 10737418240, '0.0.0.0/0')
ON DUPLICATE KEY UPDATE username = username; ON DUPLICATE KEY UPDATE username = username;