feat: PSK 版本化管理 + DB 表 + 管理后台增删启禁
- V4 迁移: psks 表 (version, secret, description, is_active) - PskService: 启动导入 PAR_MEDIABOT_PSK → v1, verify 遍历所有活跃 PSK - HmacAuthInterceptor: PSK 签名优先验签,精简为 Bearer/PSK 双路径 - AdminController: GET/POST /psks + toggle 启禁 - AuthController: register/recover 改用 PskService 验证 - docker-compose.yml 新增 PAR_MEDIABOT_PSK 环境变量 mediabot 端只需: X-Signature = HMAC(PSK, 'email:ts')
This commit is contained in:
@@ -10,7 +10,9 @@ import com.par.core.mapper.AccountMapper;
|
||||
import com.par.core.mapper.ConfigReviewMapper;
|
||||
import com.par.core.mapper.SiteConfigMapper;
|
||||
import com.par.core.mapper.SiteMapper;
|
||||
import com.par.core.entity.Psk;
|
||||
import com.par.core.service.AccountService;
|
||||
import com.par.core.service.PskService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -19,6 +21,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -36,6 +39,7 @@ public class AdminController {
|
||||
private final AccountMapper accountMapper;
|
||||
private final SiteMapper siteMapper;
|
||||
private final AccountService accountService;
|
||||
private final PskService pskService;
|
||||
|
||||
/**
|
||||
* 获取所有用户列表
|
||||
@@ -221,6 +225,34 @@ public class AdminController {
|
||||
return ApiResponse.success(accountService.listEmails(accountId));
|
||||
}
|
||||
|
||||
// ===================== PSK 管理 =====================
|
||||
|
||||
@GetMapping("/psks")
|
||||
public ApiResponse<List<Psk>> listPsks(HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
return ApiResponse.success(pskService.listAll());
|
||||
}
|
||||
|
||||
@PostMapping("/psks")
|
||||
public ApiResponse<Psk> addPsk(@RequestBody Map<String, String> body, HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
String version = body.get("version");
|
||||
String secret = body.get("secret");
|
||||
String description = body.get("description");
|
||||
if (version == null || secret == null) {
|
||||
return ApiResponse.error(400, "version and secret are required");
|
||||
}
|
||||
return ApiResponse.success(pskService.add(version, secret, description));
|
||||
}
|
||||
|
||||
@PostMapping("/psks/{id}/toggle")
|
||||
public ApiResponse<Void> togglePsk(@PathVariable Long id, @RequestParam("active") boolean active,
|
||||
HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
pskService.toggle(id, active);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
private void checkAdmin(HttpServletRequest request) {
|
||||
TrustLevel level = (TrustLevel) request.getAttribute("trustLevel");
|
||||
if (level == null || level != TrustLevel.ADMIN) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.par.api.controller;
|
||||
|
||||
import com.par.common.util.HmacUtil;
|
||||
import com.par.core.dto.AccountDTO;
|
||||
import com.par.core.dto.ApiResponse;
|
||||
import com.par.core.dto.LoginRequest;
|
||||
@@ -8,10 +7,10 @@ import com.par.core.dto.RegisterRequest;
|
||||
import com.par.core.entity.Account;
|
||||
import com.par.core.interceptor.HmacAuthInterceptor;
|
||||
import com.par.core.service.AccountService;
|
||||
import com.par.core.service.PskService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -27,13 +26,13 @@ public class AuthController {
|
||||
|
||||
private final AccountService accountService;
|
||||
private final HmacAuthInterceptor hmacAuthInterceptor;
|
||||
private final PskService pskService;
|
||||
|
||||
@Value("${par.mediabot.psk:}")
|
||||
private String mediabotPsk;
|
||||
|
||||
public AuthController(AccountService accountService, HmacAuthInterceptor hmacAuthInterceptor) {
|
||||
public AuthController(AccountService accountService, HmacAuthInterceptor hmacAuthInterceptor,
|
||||
PskService pskService) {
|
||||
this.accountService = accountService;
|
||||
this.hmacAuthInterceptor = hmacAuthInterceptor;
|
||||
this.pskService = pskService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,15 +115,14 @@ public class AuthController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 X-Medibot-Timestamp + X-Medibot-Signature
|
||||
* 验证 X-Medibot-Timestamp + X-Medibot-Signature(通过 PskService)
|
||||
*/
|
||||
private boolean verifyMediabotPsk(HttpServletRequest request, String email) {
|
||||
String ts = request.getHeader("X-Medibot-Timestamp");
|
||||
String sig = request.getHeader("X-Medibot-Signature");
|
||||
if (ts == null || sig == null) return false;
|
||||
try {
|
||||
long timestamp = Long.parseLong(ts);
|
||||
return HmacUtil.verifyMediabotPsk(email, timestamp, sig, mediabotPsk);
|
||||
return pskService.verify(email, Long.parseLong(ts), sig) != null;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user