feat: API Key 静默认证 + 多邮箱支持
核心改动: - V3 迁移: accounts 加 api_key/api_secret + account_emails 多邮箱关联表 - HMAC 验签改用 api_secret 替代密码哈希(改密码不影响集成) - findByAnyEmail 支持主邮箱 + 关联邮箱查找 - AdminController: API Key 生成/重置 + 邮箱绑定/解绑/列表 - 前端 Users.vue: API Key 列 + 生成按钮 + Secret 复制弹窗 - 现有邮箱自动迁移到 account_emails 表 认证流程: 旧: X-Email → account.email → passwordHash 作 HMAC key 新: X-Email → account_emails ∪ account.email → api_secret 作 HMAC key
This commit is contained in:
@@ -18,6 +18,8 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理员控制器
|
||||
@@ -153,6 +155,72 @@ public class AdminController {
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成/重置 API Key
|
||||
*/
|
||||
@PostMapping("/accounts/{accountId}/api-key")
|
||||
public ApiResponse<Map<String, String>> generateApiKey(
|
||||
@PathVariable Long accountId,
|
||||
HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
|
||||
var account = accountMapper.selectById(accountId);
|
||||
if (account == null) {
|
||||
return ApiResponse.error(404, "Account not found");
|
||||
}
|
||||
|
||||
String[] keys = accountService.generateApiCredentials(accountId);
|
||||
Map<String, String> result = new java.util.LinkedHashMap<>();
|
||||
result.put("apiKey", keys[0]);
|
||||
result.put("apiSecret", keys[1]);
|
||||
// secret 只在生成时返回一次,前端需要立即保存
|
||||
log.info("API key generated for account: id={}", accountId);
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定额外邮箱
|
||||
*/
|
||||
@PostMapping("/accounts/{accountId}/emails")
|
||||
public ApiResponse<Void> bindEmail(
|
||||
@PathVariable Long accountId,
|
||||
@RequestBody Map<String, String> payload,
|
||||
HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
String email = payload.get("email");
|
||||
if (email == null || email.isBlank()) {
|
||||
return ApiResponse.error(400, "email is required");
|
||||
}
|
||||
accountService.bindEmail(accountId, email);
|
||||
log.info("Email bound to account: accountId={}, email={}", accountId, email);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑邮箱
|
||||
*/
|
||||
@DeleteMapping("/accounts/{accountId}/emails/{emailId}")
|
||||
public ApiResponse<Void> unbindEmail(
|
||||
@PathVariable Long accountId,
|
||||
@PathVariable Long emailId,
|
||||
HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
accountService.unbindEmail(emailId);
|
||||
log.info("Email unbound: accountId={}, emailId={}", accountId, emailId);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取账户的绑定邮箱列表
|
||||
*/
|
||||
@GetMapping("/accounts/{accountId}/emails")
|
||||
public ApiResponse<List<String>> listEmails(
|
||||
@PathVariable Long accountId,
|
||||
HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
return ApiResponse.success(accountService.listEmails(accountId));
|
||||
}
|
||||
|
||||
private void checkAdmin(HttpServletRequest request) {
|
||||
TrustLevel level = (TrustLevel) request.getAttribute("trustLevel");
|
||||
if (level == null || level != TrustLevel.ADMIN) {
|
||||
|
||||
Reference in New Issue
Block a user