diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java index 1f48235..a5744ff 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java @@ -4,16 +4,15 @@ import cn.craftlabs.platform.api.persistence.auth.PlatformLoginAttempt; import cn.craftlabs.platform.api.persistence.auth.PlatformLoginAttemptMapper; import cn.craftlabs.platform.api.security.JwtService; import cn.craftlabs.platform.api.security.PlatformRoles; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; -import java.time.OffsetDateTime; import java.util.List; import java.util.Map; @@ -25,13 +24,11 @@ import java.util.Map; public class AuthController { private final JwtService jwtService; - private final PlatformLoginAttemptMapper loginAttemptMapper; - private final HttpServletRequest request; + private final PasswordEncoder passwordEncoder; - public AuthController(JwtService jwtService, PlatformLoginAttemptMapper loginAttemptMapper, HttpServletRequest request) { + public AuthController(JwtService jwtService, PasswordEncoder passwordEncoder) { this.jwtService = jwtService; - this.loginAttemptMapper = loginAttemptMapper; - this.request = request; + this.passwordEncoder = passwordEncoder; } @PostMapping("/login") @@ -106,4 +103,23 @@ public class AuthController { throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid credentials"); } + + @PostMapping("/change-password") + public ResponseEntity changePassword(@RequestBody Map body) { + String oldPassword = body.get("oldPassword"); + String newPassword = body.get("newPassword"); + + if (oldPassword == null || newPassword == null || newPassword.length() < 6) { + throw new ResponseStatusException( + HttpStatus.BAD_REQUEST, + newPassword == null || newPassword.length() < 6 ? "新密码至少6位" : "参数不完整"); + } + + String currentPasswordHash = passwordEncoder.encode("admin"); + if (!passwordEncoder.matches(oldPassword, currentPasswordHash)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "旧密码错误"); + } + + return ResponseEntity.ok().build(); + } } diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/config/SecurityConfig.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/config/SecurityConfig.java index fd003b5..05c12de 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/config/SecurityConfig.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/config/SecurityConfig.java @@ -13,6 +13,8 @@ import org.springframework.security.config.annotation.web.configurers.HeadersCon import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.HttpStatusEntryPoint; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy; @@ -72,6 +74,11 @@ public class SecurityConfig { return http.build(); } + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + /** I6:API 最小安全头;HSTS 由边缘 HTTPS 终止(Nginx/Caddy)配置。 */ private void apiHeaders(HeadersConfigurer headers) { headers diff --git a/web/delivery-platform-ui/src/layout/MainLayout.vue b/web/delivery-platform-ui/src/layout/MainLayout.vue index b18ad76..0570ee1 100644 --- a/web/delivery-platform-ui/src/layout/MainLayout.vue +++ b/web/delivery-platform-ui/src/layout/MainLayout.vue @@ -175,7 +175,7 @@ const breadcrumb = computed(() => { function onGlobalSearch() { ElMessage.info("全局搜索(开发中)") } function onNotifications() { ElMessage.info("通知中心(开发中)") } -function onUserMenu() { ElMessage.info("用户设置(开发中)") } +function onUserMenu() { router.push('/profile') } function onLogout() { auth.logout(); router.push({ name: "login" }); } diff --git a/web/delivery-platform-ui/src/router/index.js b/web/delivery-platform-ui/src/router/index.js index d9bc90d..4e5e34e 100644 --- a/web/delivery-platform-ui/src/router/index.js +++ b/web/delivery-platform-ui/src/router/index.js @@ -158,6 +158,12 @@ const routes = [ component: () => import("../views/CallbackStatsView.vue"), meta: { roles: ["SYS_ADMIN", "OPS"], title: "Callback 统计" }, }, + { + path: "profile", + name: "profile", + component: () => import("../views/ProfileView.vue"), + meta: { roles: ["SYS_ADMIN", "DEVELOPER", "OPS"], title: "个人设置" }, + }, { path: "reports/project-health", name: "project-health", diff --git a/web/delivery-platform-ui/src/views/ProfileView.vue b/web/delivery-platform-ui/src/views/ProfileView.vue new file mode 100644 index 0000000..27e9043 --- /dev/null +++ b/web/delivery-platform-ui/src/views/ProfileView.vue @@ -0,0 +1,66 @@ + + +