From 0a422fd504dd8b84ae668a6f9f3e66d4c5744a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=AB=9E=E7=94=B1?= <1193626695@qq.com> Date: Sun, 7 Dec 2025 22:09:18 +0800 Subject: [PATCH 1/2] =?UTF-8?q?CORS=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../campus/water/config/SecurityConfig.java | 49 +++++++++++++------ .../water/controller/AlertController.java | 11 ++--- .../campus/water/service/LoginService.java | 45 +++++++++-------- 3 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/campus/water/config/SecurityConfig.java b/src/main/java/com/campus/water/config/SecurityConfig.java index c94cbdc..363bbbd 100644 --- a/src/main/java/com/campus/water/config/SecurityConfig.java +++ b/src/main/java/com/campus/water/config/SecurityConfig.java @@ -4,7 +4,6 @@ package com.campus.water.config; import com.campus.water.security.JwtAuthenticationFilter; import com.campus.water.security.RoleConstants; import com.campus.water.security.UserDetailsServiceImpl; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; @@ -18,6 +17,12 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; +import java.util.Collections; /** * 安全配置类 @@ -27,11 +32,15 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic @EnableMethodSecurity(prePostEnabled = true) // 启用方法级权限控制 public class SecurityConfig { - @Autowired - private UserDetailsServiceImpl userDetailsService; + private final UserDetailsServiceImpl userDetailsService; + private final JwtAuthenticationFilter jwtAuthenticationFilter; - @Autowired - private JwtAuthenticationFilter jwtAuthenticationFilter; + // 构造函数注入(替代@Autowired字段注入,更安全) + public SecurityConfig(UserDetailsServiceImpl userDetailsService, + JwtAuthenticationFilter jwtAuthenticationFilter) { + this.userDetailsService = userDetailsService; + this.jwtAuthenticationFilter = jwtAuthenticationFilter; + } /** * 认证提供者 @@ -66,14 +75,12 @@ public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http - // 关闭CSRF - .csrf(csrf -> csrf.disable()) - // 无状态会话 - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - // 权限控制 + .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 启用CORS并配置源 + .csrf(csrf -> csrf.disable()) // 关闭CSRF + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 无状态会话 .authorizeHttpRequests(auth -> auth // 登录接口放行 - .requestMatchers("/api/app/student/login", "/api/app/repair/login", "/api/web/login").permitAll() + .requestMatchers("/api/app/student/login", "/api/app/repair/login", "/api/web/login", "/api/common/login").permitAll() // 静态资源放行 .requestMatchers("/static/**", "/templates/**").permitAll() // 新增告警接口权限控制(URL级) @@ -85,12 +92,24 @@ public class SecurityConfig { // 其他接口需要认证 .anyRequest().authenticated() ) - // 添加JWT过滤器 - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); // 添加JWT过滤器 - // 设置认证提供者 - http.authenticationProvider(authenticationProvider()); + http.authenticationProvider(authenticationProvider()); // 设置认证提供者 return http.build(); } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(Collections.singletonList("*")); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/api/**", configuration); + return source; + } } \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/AlertController.java b/src/main/java/com/campus/water/controller/AlertController.java index f12fde9..dd2e766 100644 --- a/src/main/java/com/campus/water/controller/AlertController.java +++ b/src/main/java/com/campus/water/controller/AlertController.java @@ -19,16 +19,16 @@ import java.util.List; @RestController @RequestMapping("/api/alerts") @RequiredArgsConstructor -@Tag(name = "告警管理接口") // 替换 @Api +@Tag(name = "告警管理接口") public class AlertController { private final AlertRepository alertRepository; @GetMapping("/history") @PreAuthorize("hasAnyRole('ADMIN', 'REPAIRMAN')") - @Operation(summary = "分页查询告警历史(支持多条件筛选)") // 替换 @ApiOperation(若有) + @Operation(summary = "分页查询告警历史(支持多条件筛选)") public ResultVO> getAlertHistory( - @Parameter(description = "设备ID(可选)") @RequestParam(required = false) String deviceId, // 替换 @ApiParam + @Parameter(description = "设备ID(可选)") @RequestParam(required = false) String deviceId, @Parameter(description = "告警级别(可选,如error、critical)") @RequestParam(required = false) String level, @Parameter(description = "告警状态(可选,如pending、resolved)") @RequestParam(required = false) String status, @Parameter(description = "开始时间(可选,格式:yyyy-MM-dd HH:mm:ss)") @RequestParam(required = false) LocalDateTime startTime, @@ -37,7 +37,6 @@ public class AlertController { ) { List alerts; - // 构建查询条件(根据参数动态拼接,实际可使用Specification更灵活) if (deviceId != null) { alerts = alertRepository.findByDeviceIdAndTimestampBetween(deviceId, startTime, endTime); } else if (level != null) { @@ -61,12 +60,12 @@ public class AlertController { @GetMapping("/pending") @PreAuthorize("hasAnyRole('ADMIN', 'REPAIRMAN')") public ResultVO> getPendingAlerts( - @Parameter(description = "区域ID(可选)") @RequestParam(required = false) String areaId) { // 替换@ApiParam为@Parameter + @Parameter(description = "区域ID(可选)") @RequestParam(required = false) String areaId) { List pendingAlerts = areaId != null ? alertRepository.findByAreaIdAndStatus(areaId, Alert.AlertStatus.pending) : alertRepository.findByStatus(Alert.AlertStatus.pending); - // 按优先级排序(紧急在前) + // 按优先级排序(紧急在前)- 使用方法引用替代lambda pendingAlerts.sort((a1, a2) -> Integer.compare(a2.getAlertLevel().getPriority(), a1.getAlertLevel().getPriority())); diff --git a/src/main/java/com/campus/water/service/LoginService.java b/src/main/java/com/campus/water/service/LoginService.java index fb78143..9f3e184 100644 --- a/src/main/java/com/campus/water/service/LoginService.java +++ b/src/main/java/com/campus/water/service/LoginService.java @@ -1,18 +1,17 @@ package com.campus.water.service; -import com.campus.water.entity.Admin; // 改为引用修改import com.campus.water.entity.dto.request.LoginRequest; +import com.campus.water.entity.Admin; import com.campus.water.entity.po.RepairerAuthPO; import com.campus.water.entity.po.UserPO; import com.campus.water.entity.vo.LoginVO; import com.campus.water.mapper.AdminRepository; import com.campus.water.mapper.RepairerAuthRepository; import com.campus.water.mapper.UserRepository; +import com.campus.water.entity.dto.request.LoginRequest; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import org.springframework.util.DigestUtils; -import com.campus.water.entity.dto.request.LoginRequest; -import java.nio.charset.StandardCharsets; import java.util.UUID; @Service @@ -27,64 +26,70 @@ public class LoginService { @Autowired private RepairerAuthRepository repairerAuthRepository; + @Autowired + private PasswordEncoder passwordEncoder; // 使用Security配置的密码加密器 + public LoginVO login(LoginRequest loginRequest) { String username = loginRequest.getUsername(); - // 密码MD5加密(和数据库存储的一致) - String encryptedPwd = DigestUtils.md5DigestAsHex( - loginRequest.getPassword().getBytes(StandardCharsets.UTF_8) - ); + String password = loginRequest.getPassword(); // 不再手动MD5加密,使用Security的加密器 String userType = loginRequest.getUserType(); switch (userType) { case "admin": - return handleAdminLogin(username, encryptedPwd); + return handleAdminLogin(username, password); case "user": - return handleUserLogin(username, encryptedPwd); + return handleUserLogin(username, password); case "repairer": - return handleRepairerLogin(username, encryptedPwd); + return handleRepairerLogin(username, password); default: throw new RuntimeException("无效的用户类型:" + userType); } } - // 管理员登录(适配修改后的Admin实体和AdminRepository) + // 管理员登录 private LoginVO handleAdminLogin(String username, String password) { - // 方法名从findByUsername改为findByAdminName Admin admin = adminRepository.findByAdminName(username) .orElseThrow(() -> new RuntimeException("管理员不存在")); - if (!admin.getPassword().equals(password)) { + + // 使用密码加密器验证密码 + if (!passwordEncoder.matches(password, admin.getPassword())) { throw new RuntimeException("密码错误"); } + return createLoginVO(admin.getAdminId(), username, "admin"); } - // 学生登录(保持不变) + // 学生登录 private LoginVO handleUserLogin(String username, String password) { UserPO user = userRepository.findByUsername(username) .orElseThrow(() -> new RuntimeException("用户不存在")); - if (!user.getPassword().equals(password)) { + + if (!passwordEncoder.matches(password, user.getPassword())) { throw new RuntimeException("密码错误"); } + return createLoginVO(user.getStudentId(), username, "user"); } - // 维修人员登录(保持不变) + // 维修人员登录 private LoginVO handleRepairerLogin(String username, String password) { RepairerAuthPO repairer = repairerAuthRepository.findByUsername(username) .orElseThrow(() -> new RuntimeException("维修人员不存在")); - if (!repairer.getPassword().equals(password)) { + + if (!passwordEncoder.matches(password, repairer.getPassword())) { throw new RuntimeException("密码错误"); } + return createLoginVO(repairer.getRepairmanId(), username, "repairer"); } - // 构建登录响应VO(保持不变) + // 构建登录响应VO private LoginVO createLoginVO(String userId, String username, String userType) { LoginVO vo = new LoginVO(); vo.setUserId(userId); vo.setUsername(username); vo.setUserType(userType); - // 临时Token(后续可替换为JWT) + // 后续应替换为JWT生成逻辑 vo.setToken(UUID.randomUUID().toString().replace("-", "")); return vo; } -- 2.34.1 From e0c6b9e9b8239ccef3b9be8115bd4f86f1f3697d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=AB=9E=E7=94=B1?= <1193626695@qq.com> Date: Sun, 7 Dec 2025 22:14:42 +0800 Subject: [PATCH 2/2] =?UTF-8?q?CORS=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../campus/water/config/SecurityConfig.java | 1 - .../campus/water/service/LoginService.java | 44 +++++++------------ 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/campus/water/config/SecurityConfig.java b/src/main/java/com/campus/water/config/SecurityConfig.java index 363bbbd..4a0b98f 100644 --- a/src/main/java/com/campus/water/config/SecurityConfig.java +++ b/src/main/java/com/campus/water/config/SecurityConfig.java @@ -2,7 +2,6 @@ package com.campus.water.config; import com.campus.water.security.JwtAuthenticationFilter; -import com.campus.water.security.RoleConstants; import com.campus.water.security.UserDetailsServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/campus/water/service/LoginService.java b/src/main/java/com/campus/water/service/LoginService.java index 9f3e184..cae1572 100644 --- a/src/main/java/com/campus/water/service/LoginService.java +++ b/src/main/java/com/campus/water/service/LoginService.java @@ -8,50 +8,40 @@ import com.campus.water.mapper.AdminRepository; import com.campus.water.mapper.RepairerAuthRepository; import com.campus.water.mapper.UserRepository; import com.campus.water.entity.dto.request.LoginRequest; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.util.UUID; @Service +@RequiredArgsConstructor // 自动生成构造函数(需要Lombok依赖) public class LoginService { - @Autowired - private AdminRepository adminRepository; - - @Autowired - private UserRepository userRepository; - - @Autowired - private RepairerAuthRepository repairerAuthRepository; - - @Autowired - private PasswordEncoder passwordEncoder; // 使用Security配置的密码加密器 + // 依赖注入:通过构造函数初始化(@RequiredArgsConstructor自动生成构造函数) + private final AdminRepository adminRepository; + private final UserRepository userRepository; + private final RepairerAuthRepository repairerAuthRepository; + private final PasswordEncoder passwordEncoder; public LoginVO login(LoginRequest loginRequest) { String username = loginRequest.getUsername(); - String password = loginRequest.getPassword(); // 不再手动MD5加密,使用Security的加密器 + String password = loginRequest.getPassword(); String userType = loginRequest.getUserType(); - switch (userType) { - case "admin": - return handleAdminLogin(username, password); - case "user": - return handleUserLogin(username, password); - case "repairer": - return handleRepairerLogin(username, password); - default: - throw new RuntimeException("无效的用户类型:" + userType); - } + // 增强版switch(解决"Switch语句可替换为增强的switch"警告) + return switch (userType) { + case "admin" -> handleAdminLogin(username, password); + case "user" -> handleUserLogin(username, password); + case "repairer" -> handleRepairerLogin(username, password); + default -> throw new RuntimeException("无效的用户类型:" + userType); + }; } - // 管理员登录 private LoginVO handleAdminLogin(String username, String password) { Admin admin = adminRepository.findByAdminName(username) .orElseThrow(() -> new RuntimeException("管理员不存在")); - // 使用密码加密器验证密码 if (!passwordEncoder.matches(password, admin.getPassword())) { throw new RuntimeException("密码错误"); } @@ -59,7 +49,6 @@ public class LoginService { return createLoginVO(admin.getAdminId(), username, "admin"); } - // 学生登录 private LoginVO handleUserLogin(String username, String password) { UserPO user = userRepository.findByUsername(username) .orElseThrow(() -> new RuntimeException("用户不存在")); @@ -71,7 +60,6 @@ public class LoginService { return createLoginVO(user.getStudentId(), username, "user"); } - // 维修人员登录 private LoginVO handleRepairerLogin(String username, String password) { RepairerAuthPO repairer = repairerAuthRepository.findByUsername(username) .orElseThrow(() -> new RuntimeException("维修人员不存在")); @@ -83,13 +71,11 @@ public class LoginService { return createLoginVO(repairer.getRepairmanId(), username, "repairer"); } - // 构建登录响应VO private LoginVO createLoginVO(String userId, String username, String userType) { LoginVO vo = new LoginVO(); vo.setUserId(userId); vo.setUsername(username); vo.setUserType(userType); - // 后续应替换为JWT生成逻辑 vo.setToken(UUID.randomUUID().toString().replace("-", "")); return vo; } -- 2.34.1