From 27994afa9b607349d15a6b2a9aa39a6f67719be6 Mon Sep 17 00:00:00 2001 From: liejiu946 Date: Fri, 7 Nov 2025 16:40:12 +0800 Subject: [PATCH] =?UTF-8?q?opt:1.=E5=A2=9E=E5=8A=A0=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E3=80=822.=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=B3=BB=E7=BB=9F=E6=97=A5=E5=BF=97=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 ++ .../java/org/nl/apt15e/Apt15EApplication.java | 1 + .../controller/AnomalyInfoController.java | 3 +- .../map/service/impl/MapInfoServiceImpl.java | 2 +- .../nl/apt15e/apt/route/dao/RouteInfo.java | 2 +- .../service/impl/TeachingServiceImpl.java | 2 +- .../org/nl/apt15e/apt/vehicle/ProcessZip.java | 70 +++++++++++++-- .../common/logging/aspect/LogAspect.java | 73 ++++++++++----- .../apt15e/config/satoken/SaTokenConfig.java | 32 +++++++ .../config/satoken/SecurityProperties.java | 21 +++++ .../logging/controller/LogController.java | 74 +++++++++++++++ .../apt15e/manage/logging/dto/LogFileDTO.java | 34 +++++++ .../manage/logging/service/LogService.java | 81 +++++++++++++++++ .../controller/AuthorizationController.java | 44 +++++++++ .../manage/secutiry/dto/AuthUserDto.java | 18 ++++ .../service/AuthorizationService.java | 49 ++++++++++ .../org/nl/apt15e/manage/user/dao/User.java | 22 +++++ .../manage/user/service/UserService.java | 14 +++ .../user/service/impl/UserServiceImpl.java | 17 ++++ .../user/service/mapper/UserMapper.java | 14 +++ .../java/org/nl/apt15e/util/FileConstant.java | 27 ++++++ src/main/java/org/nl/apt15e/util/MD5Util.java | 20 +++++ .../java/org/nl/apt15e/util/RsaUtils.java | 90 +++++++++++++++++++ src/main/resources/config/application-dev.yml | 19 +++- .../resources/config/application-prod.yml | 19 +++- src/main/resources/config/application.yml | 33 ++++++- .../resources/language/buss/buss.properties | 3 +- .../language/buss/buss_en_US.properties | 3 +- .../language/buss/buss_zh_CN.properties | 3 +- src/main/resources/logback-spring.xml | 24 ++--- 30 files changed, 773 insertions(+), 48 deletions(-) create mode 100644 src/main/java/org/nl/apt15e/config/satoken/SaTokenConfig.java create mode 100644 src/main/java/org/nl/apt15e/config/satoken/SecurityProperties.java create mode 100644 src/main/java/org/nl/apt15e/manage/logging/controller/LogController.java create mode 100644 src/main/java/org/nl/apt15e/manage/logging/dto/LogFileDTO.java create mode 100644 src/main/java/org/nl/apt15e/manage/logging/service/LogService.java create mode 100644 src/main/java/org/nl/apt15e/manage/secutiry/controller/AuthorizationController.java create mode 100644 src/main/java/org/nl/apt15e/manage/secutiry/dto/AuthUserDto.java create mode 100644 src/main/java/org/nl/apt15e/manage/secutiry/service/AuthorizationService.java create mode 100644 src/main/java/org/nl/apt15e/manage/user/dao/User.java create mode 100644 src/main/java/org/nl/apt15e/manage/user/service/UserService.java create mode 100644 src/main/java/org/nl/apt15e/manage/user/service/impl/UserServiceImpl.java create mode 100644 src/main/java/org/nl/apt15e/manage/user/service/mapper/UserMapper.java create mode 100644 src/main/java/org/nl/apt15e/util/FileConstant.java create mode 100644 src/main/java/org/nl/apt15e/util/MD5Util.java create mode 100644 src/main/java/org/nl/apt15e/util/RsaUtils.java diff --git a/pom.xml b/pom.xml index fb40057..7c96930 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,13 @@ 3.5.2 + + + cn.dev33 + sa-token-spring-boot-starter + 1.34.0 + + org.aspectj aspectjweaver diff --git a/src/main/java/org/nl/apt15e/Apt15EApplication.java b/src/main/java/org/nl/apt15e/Apt15EApplication.java index cf11d84..0e270ba 100644 --- a/src/main/java/org/nl/apt15e/Apt15EApplication.java +++ b/src/main/java/org/nl/apt15e/Apt15EApplication.java @@ -4,6 +4,7 @@ import org.mybatis.spring.annotation.MapperScan; import org.nl.apt15e.config.SpringContextHolder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; diff --git a/src/main/java/org/nl/apt15e/apt/anomalyInfo/controller/AnomalyInfoController.java b/src/main/java/org/nl/apt15e/apt/anomalyInfo/controller/AnomalyInfoController.java index 0d531d4..2d27091 100644 --- a/src/main/java/org/nl/apt15e/apt/anomalyInfo/controller/AnomalyInfoController.java +++ b/src/main/java/org/nl/apt15e/apt/anomalyInfo/controller/AnomalyInfoController.java @@ -13,6 +13,7 @@ import org.nl.apt15e.common.excel.ErrorHandlingListener; import org.nl.apt15e.common.excel.ErrorInfoListener; import org.nl.apt15e.config.file.FileProperties; import org.nl.apt15e.config.language.LangProcess; +import org.nl.apt15e.util.FileConstant; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -104,7 +105,7 @@ public class AnomalyInfoController { try { // 创建上传目录 - File uploadDir = new File(properties.getPath().getPath()); + File uploadDir = new File(FileConstant.ERROR_IMAGE_PATH); if (!uploadDir.exists()) { uploadDir.mkdirs(); } diff --git a/src/main/java/org/nl/apt15e/apt/map/service/impl/MapInfoServiceImpl.java b/src/main/java/org/nl/apt15e/apt/map/service/impl/MapInfoServiceImpl.java index 5396f79..7e96a1d 100644 --- a/src/main/java/org/nl/apt15e/apt/map/service/impl/MapInfoServiceImpl.java +++ b/src/main/java/org/nl/apt15e/apt/map/service/impl/MapInfoServiceImpl.java @@ -68,7 +68,7 @@ public class MapInfoServiceImpl extends ServiceImpl impl } } catch (Exception e) { log.info("同步地图失败:{}",e.getMessage()); - throw new BadRequestException(LangProcess.msg("failed")); + throw new BadRequestException(LangProcess.msg("failed")+":"+e.getMessage()); } } } diff --git a/src/main/java/org/nl/apt15e/apt/route/dao/RouteInfo.java b/src/main/java/org/nl/apt15e/apt/route/dao/RouteInfo.java index f313a73..2f90768 100644 --- a/src/main/java/org/nl/apt15e/apt/route/dao/RouteInfo.java +++ b/src/main/java/org/nl/apt15e/apt/route/dao/RouteInfo.java @@ -49,7 +49,7 @@ public class RouteInfo { private Double end_y; /** - * 导航模式 前进模式, 后退模式 ,无头模式 + * 导航模式 前进模式, 后退模式 ,双向模式 */ private String navigation_mode; diff --git a/src/main/java/org/nl/apt15e/apt/teaching/service/impl/TeachingServiceImpl.java b/src/main/java/org/nl/apt15e/apt/teaching/service/impl/TeachingServiceImpl.java index 7f717d8..0b992c7 100644 --- a/src/main/java/org/nl/apt15e/apt/teaching/service/impl/TeachingServiceImpl.java +++ b/src/main/java/org/nl/apt15e/apt/teaching/service/impl/TeachingServiceImpl.java @@ -301,7 +301,7 @@ public class TeachingServiceImpl implements TeachingService { try { response = HttpRequest.post(URLConstant.RCS_IP_PORT+"/map/uploadFile") .setConnectionTimeout(3000) - .setReadTimeout(3000) + .setReadTimeout(30000) .header("token", "admin123") .header("name", "lx-script") .header("Content-Type", "multipart/form-data;charset=UTF-8") diff --git a/src/main/java/org/nl/apt15e/apt/vehicle/ProcessZip.java b/src/main/java/org/nl/apt15e/apt/vehicle/ProcessZip.java index 51c66f6..111c682 100644 --- a/src/main/java/org/nl/apt15e/apt/vehicle/ProcessZip.java +++ b/src/main/java/org/nl/apt15e/apt/vehicle/ProcessZip.java @@ -19,6 +19,7 @@ import org.nl.apt15e.apt.teaching.service.impl.TeachingServiceImpl; import org.nl.apt15e.common.BadRequestException; import org.nl.apt15e.config.file.FileProperties; import org.nl.apt15e.config.language.LangProcess; +import org.nl.apt15e.util.FileConstant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,10 +36,8 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.*; import java.util.List; -import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -69,6 +68,8 @@ public class ProcessZip { private static final String PGM_MAGIC_NUMBER = "P5"; + private List routeInfoList = new ArrayList<>(); + @Transactional(rollbackFor = Exception.class) public void processZipResponse(byte[] zipBytes) { try (ByteArrayInputStream bais = new ByteArrayInputStream(zipBytes); @@ -97,6 +98,8 @@ public class ProcessZip { String line; int lineNumber = 0; + // 清除路径信息 + routeInfoList.clear(); while ((line = reader.readLine()) != null) { lineNumber++; // 5. 处理每一行数据 @@ -105,6 +108,12 @@ public class ProcessZip { if (lineNumber == 0) { throw new BadRequestException(LangProcess.msg("error_map_pack_isNull")); } + // 批量保存路径信息 + if (ObjectUtil.isNotEmpty(routeInfoList)){ + routeInfoList = mergeBidirectionalRoutes(routeInfoList); + routeInfoService.saveBatch(routeInfoList); + } + } // 点云图 .pgm 文件转换 .png if (entryName.toLowerCase().endsWith(".pgm")) { @@ -325,7 +334,8 @@ public class ProcessZip { routeInfo.setEnd_y(Double.valueOf(processData[7])); routeInfo.setNavigation_mode(processData[16].replaceAll("^\"|\"$", "")); routeInfo.setRoute_type(processData[17].replaceAll("^\"|\"$", "")); - routeInfoService.save(routeInfo); + routeInfoList.add(routeInfo); +// routeInfoService.save(routeInfo); System.out.printf("行号 %d - 有效数据: %s%n", lineNumber, routeInfo); } @@ -457,7 +467,7 @@ public class ProcessZip { } /** - * 判断是否是图片文件 + * 判断是否是图片文件或者视频文件 * @param filename * @return */ @@ -468,7 +478,8 @@ public class ProcessZip { lowerCaseFilename.endsWith(".png") || lowerCaseFilename.endsWith(".gif") || lowerCaseFilename.endsWith(".bmp") || - lowerCaseFilename.endsWith(".webp"); + lowerCaseFilename.endsWith(".webp")|| + lowerCaseFilename.endsWith(".mp4"); } /** @@ -477,7 +488,7 @@ public class ProcessZip { * @return */ private boolean isFileExists(String filename) { - File targetFile = new File(properties.getPath().getPath() + filename); + File targetFile = new File(FileConstant.ERROR_IMAGE_PATH+ "/" + filename); return targetFile.exists(); } @@ -489,7 +500,7 @@ public class ProcessZip { */ private boolean saveImageFile(InputStream inputStream, String filename) { try { - File outputFile = new File(properties.getPath().getPath() + filename); + File outputFile = new File(FileConstant.ERROR_IMAGE_PATH + "/" + filename); // 确保目录存在 File parentDir = outputFile.getParentFile(); @@ -504,4 +515,47 @@ public class ProcessZip { return false; } } + + public static List mergeBidirectionalRoutes(List originalData) { + List mergedData = new ArrayList<>(); + Map routeMap = new HashMap<>(); + Set processedIds = new HashSet<>(); + + // 第一次遍历:构建快速查找映射 + for (RouteInfo route : originalData) { + String key = route.getStart_id() + "-" + route.getEnd_id(); + routeMap.put(key, route); + } + + // 第二次遍历:查找并合并双向路径 + for (RouteInfo route : originalData) { + if (processedIds.contains(route.getRoute_id())) { + continue; + } + +// String forwardKey = route.getStart_id() + "-" + route.getEnd_id(); + String reverseKey = route.getEnd_id() + "-" + route.getStart_id(); + + // 检查是否存在精确反向路径 + if (routeMap.containsKey(reverseKey)) { + RouteInfo reverseRoute = routeMap.get(reverseKey); + + if (!processedIds.contains(reverseRoute.getRoute_id())) { + // 选择routeId较小的作为主记录,导航模式改为2(双向) + RouteInfo bidirectionalRoute = route.getRoute_id() < reverseRoute.getRoute_id() ? route : reverseRoute; + bidirectionalRoute.setNavigation_mode("2"); + mergedData.add(bidirectionalRoute); + + processedIds.add(route.getRoute_id()); + processedIds.add(reverseRoute.getRoute_id()); + } + } else { + // 没有反向路径,直接添加 + mergedData.add(route); + processedIds.add(route.getRoute_id()); + } + } + + return mergedData; + } } diff --git a/src/main/java/org/nl/apt15e/common/logging/aspect/LogAspect.java b/src/main/java/org/nl/apt15e/common/logging/aspect/LogAspect.java index 5d38b61..fce0306 100644 --- a/src/main/java/org/nl/apt15e/common/logging/aspect/LogAspect.java +++ b/src/main/java/org/nl/apt15e/common/logging/aspect/LogAspect.java @@ -12,20 +12,13 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; +import org.nl.apt15e.common.logging.annotation.Log; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.stereotype.Component; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; + /** * @author dsh @@ -44,23 +37,63 @@ public class LogAspect { /** * 环绕通知 - * @param joinPoint + * @param point * @return * @throws Throwable */ @Around("logPointCut()") - public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { -// MethodSignature signature = (MethodSignature) joinPoint.getSignature(); - -// String params = JSONObject.toJSONString(joinPoint.getArgs()); + public Object logAround(ProceedingJoinPoint point) throws Throwable { +//// MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // -// - Object result; -// log.info("【日志注解】开始执行 -- {}:{} {}", className, methodName, params); - result = joinPoint.proceed(); -// log.info("返回参数:{}" ,JSONObject.toJSONString(result)); +//// String params = JSONObject.toJSONString(joinPoint.getArgs()); +//// +//// +// Object result; +//// log.info("【日志注解】开始执行 -- {}:{} {}", className, methodName, params); +// result = joinPoint.proceed(); +//// log.info("返回参数:{}" ,JSONObject.toJSONString(result)); +// return result; + Object result = null; + long beginTime = System.currentTimeMillis(); + try { + // 执行方法 + result = point.proceed(); + } catch (Throwable e) { + e.printStackTrace(); + } + // 执行时长(毫秒) + long time = System.currentTimeMillis() - beginTime; + // 保存日志 + saveLog(point, time); return result; } + private void saveLog(ProceedingJoinPoint joinPoint, long time) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + Log logAnnotation = method.getAnnotation(Log.class); + // 注解上的描述 + String description = ""; + if (logAnnotation != null) { + description = logAnnotation.value(); + } + // 请求的方法名 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = signature.getName(); + // 请求的方法参数值 + Object[] args = joinPoint.getArgs(); + // 请求的方法参数名称 + LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); + String[] paramNames = u.getParameterNames(method); + String params = ""; + if (args != null && paramNames != null) { + + for (int i = 0; i < args.length; i++) { + params += " " + paramNames[i] + ": " + args[i]; + } + } + log.info("【日志注解】开始执行 -- 描述:{} -- 类方法:{}.{}() -- 参数:{} -- 执行时长:{}毫秒", description, className, methodName, params, time); + } + } diff --git a/src/main/java/org/nl/apt15e/config/satoken/SaTokenConfig.java b/src/main/java/org/nl/apt15e/config/satoken/SaTokenConfig.java new file mode 100644 index 0000000..75a170a --- /dev/null +++ b/src/main/java/org/nl/apt15e/config/satoken/SaTokenConfig.java @@ -0,0 +1,32 @@ +package org.nl.apt15e.config.satoken; + +import cn.dev33.satoken.interceptor.SaInterceptor; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.stp.StpUtil; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.annotation.Resource; + +/** + * @author dsh + * 2025/10/22 + */ +@Configuration +public class SaTokenConfig implements WebMvcConfigurer { + + @Resource + private SecurityProperties securityProperties; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new SaInterceptor(handle -> { + SaRouter + .match("/**") + .check(r -> StpUtil.checkLogin()); + })) + .addPathPatterns("/**") + .excludePathPatterns(securityProperties.getExcludes()); + } +} diff --git a/src/main/java/org/nl/apt15e/config/satoken/SecurityProperties.java b/src/main/java/org/nl/apt15e/config/satoken/SecurityProperties.java new file mode 100644 index 0000000..3f0269b --- /dev/null +++ b/src/main/java/org/nl/apt15e/config/satoken/SecurityProperties.java @@ -0,0 +1,21 @@ +package org.nl.apt15e.config.satoken; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * @author dsh + * 2025/10/22 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "security") +public class SecurityProperties { + + /** + * 排除路径 + */ + private String[] excludes; + +} diff --git a/src/main/java/org/nl/apt15e/manage/logging/controller/LogController.java b/src/main/java/org/nl/apt15e/manage/logging/controller/LogController.java new file mode 100644 index 0000000..d26c919 --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/logging/controller/LogController.java @@ -0,0 +1,74 @@ +package org.nl.apt15e.manage.logging.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import org.nl.apt15e.common.BadRequestException; +import org.nl.apt15e.common.logging.annotation.Log; +import org.nl.apt15e.manage.logging.dto.LogFileDTO; +import org.nl.apt15e.manage.logging.service.LogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +/** + * @author dsh + * 2025/11/3 + */ +@RestController +@RequestMapping("/api/logs") +public class LogController { + + @Autowired + private LogService logService; + + /** + * 获取日志文件列表 + */ + @Log("获取日志文件列表") + @GetMapping("/list") + public ResponseEntity getLogFiles() { + try { + return new ResponseEntity<>(logService.getLogFiles(), HttpStatus.OK); + } catch (Exception e) { + throw new BadRequestException("获取日志文件失败:"+e.getMessage()); + } + } + + /** + * 下载日志文件 + */ + @Log("下载日志文件") + @GetMapping("/download/{fileName}") + public ResponseEntity downloadLogFile(@PathVariable String fileName) { + try { + File file = logService.getLogFile(fileName); + + Path filePath = file.toPath(); + Resource resource = new InputStreamResource(Files.newInputStream(filePath)); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + file.getName() + "\"") + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .contentLength(file.length()) + .body(resource); + + } catch (RuntimeException e) { + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } +} diff --git a/src/main/java/org/nl/apt15e/manage/logging/dto/LogFileDTO.java b/src/main/java/org/nl/apt15e/manage/logging/dto/LogFileDTO.java new file mode 100644 index 0000000..2f6b06c --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/logging/dto/LogFileDTO.java @@ -0,0 +1,34 @@ +package org.nl.apt15e.manage.logging.dto; + +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @author dsh + * 2025/11/3 + */ +@Data +public class LogFileDTO { + private String fileName; + private String filePath; + private Long fileSize; + private LocalDateTime lastModified; + private String readableSize; + + public LogFileDTO(String fileName, String filePath, Long fileSize, LocalDateTime lastModified) { + this.fileName = fileName; + this.filePath = filePath; + this.fileSize = fileSize; + this.lastModified = lastModified; + this.readableSize = formatFileSize(fileSize); + } + + private String formatFileSize(long size) { + if (size <= 0) return "0 B"; + final String[] units = new String[]{"B", "KB", "MB", "GB"}; + int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); + return String.format("%.1f %s", size / Math.pow(1024, digitGroups), units[digitGroups]); + } + +} diff --git a/src/main/java/org/nl/apt15e/manage/logging/service/LogService.java b/src/main/java/org/nl/apt15e/manage/logging/service/LogService.java new file mode 100644 index 0000000..81cbe37 --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/logging/service/LogService.java @@ -0,0 +1,81 @@ +package org.nl.apt15e.manage.logging.service; + +import org.nl.apt15e.manage.logging.dto.LogFileDTO; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; + +/** + * @author dsh + * 2025/11/3 + */ +@Service +public class LogService { + + @Value("${customize.log-dir}") + private String logDir; + + /** + * 获取日志文件列表 + */ + public List getLogFiles() { + List logFiles = new ArrayList<>(); + File directory = new File(logDir); + + if (!directory.exists() || !directory.isDirectory()) { + return logFiles; + } + + File[] files = directory.listFiles((dir, name) -> + name.endsWith(".log") || name.endsWith(".txt")); + + if (files != null) { + for (File file : files) { + if (file.isFile()) { + LogFileDTO logFile = new LogFileDTO( + file.getName(), + file.getAbsolutePath(), + file.length(), + LocalDateTime.ofInstant( + Instant.ofEpochMilli(file.lastModified()), + ZoneId.systemDefault() + ) + ); + logFiles.add(logFile); + } + } + } + + // 按修改时间倒序排列 + logFiles.sort((a, b) -> b.getLastModified().compareTo(a.getLastModified())); + return logFiles; + } + + /** + * 获取日志文件 + */ + public File getLogFile(String fileName) { + // 防止路径遍历攻击 + if (fileName.contains("..") || fileName.contains("/") || fileName.contains("\\")) { + throw new IllegalArgumentException("Invalid file name"); + } + + File file = new File(logDir, fileName); + if (!file.exists() || !file.isFile()) { + throw new RuntimeException("File not found: " + fileName); + } + + // 确保文件在日志目录内 + if (!file.getAbsolutePath().startsWith(new File(logDir).getAbsolutePath())) { + throw new SecurityException("Access denied"); + } + + return file; + } +} diff --git a/src/main/java/org/nl/apt15e/manage/secutiry/controller/AuthorizationController.java b/src/main/java/org/nl/apt15e/manage/secutiry/controller/AuthorizationController.java new file mode 100644 index 0000000..605e0cf --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/secutiry/controller/AuthorizationController.java @@ -0,0 +1,44 @@ +package org.nl.apt15e.manage.secutiry.controller; + +import cn.dev33.satoken.stp.SaTokenInfo; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.nl.apt15e.common.BadRequestException; +import org.nl.apt15e.config.language.LangProcess; +import org.nl.apt15e.manage.secutiry.dto.AuthUserDto; +import org.nl.apt15e.manage.secutiry.service.AuthorizationService; +import org.nl.apt15e.util.RsaUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +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 javax.annotation.Resource; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author dsh + * 2025/10/22 + */ +@RestController +@RequestMapping("/auth") +public class AuthorizationController { + + @Resource + private AuthorizationService authorizationService; + + @PostMapping(value = "/login") + public ResponseEntity login(@RequestBody AuthUserDto user) { + + if (ObjectUtil.isEmpty(user)) { + throw new BadRequestException(LangProcess.msg("param_is_null")); + } + return new ResponseEntity<>(authorizationService.login(user), HttpStatus.OK); + } + +} diff --git a/src/main/java/org/nl/apt15e/manage/secutiry/dto/AuthUserDto.java b/src/main/java/org/nl/apt15e/manage/secutiry/dto/AuthUserDto.java new file mode 100644 index 0000000..f3bcafa --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/secutiry/dto/AuthUserDto.java @@ -0,0 +1,18 @@ +package org.nl.apt15e.manage.secutiry.dto; + +import lombok.Data; +import org.jetbrains.annotations.NotNull; + +/** + * @author dsh + * 2025/10/23 + */ +@Data +public class AuthUserDto { + + private String userName; + + private String password; + + +} diff --git a/src/main/java/org/nl/apt15e/manage/secutiry/service/AuthorizationService.java b/src/main/java/org/nl/apt15e/manage/secutiry/service/AuthorizationService.java new file mode 100644 index 0000000..fbd0d28 --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/secutiry/service/AuthorizationService.java @@ -0,0 +1,49 @@ +package org.nl.apt15e.manage.secutiry.service; + +import cn.dev33.satoken.secure.SaSecureUtil; +import cn.dev33.satoken.stp.SaLoginModel; +import cn.dev33.satoken.stp.SaTokenInfo; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.SneakyThrows; +import org.nl.apt15e.common.BadRequestException; +import org.nl.apt15e.config.language.LangProcess; +import org.nl.apt15e.manage.secutiry.dto.AuthUserDto; +import org.nl.apt15e.manage.user.dao.User; +import org.nl.apt15e.manage.user.service.UserService; +import org.nl.apt15e.util.RsaUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author dsh + * 2025/10/23 + */ +@Service +public class AuthorizationService{ + + @Resource + private UserService userService; + + @SneakyThrows + public Map login(AuthUserDto user){ + // 密码解密 - 前端的加密规则: encrypt(根据实际更改) + String password = RsaUtils.decryptByPrivateKey(RsaUtils.privateKey, user.getPassword()); + user.setPassword(password); + User userData = userService.getOne(new LambdaQueryWrapper().eq(User::getUserName, user.getUserName())); + if (ObjectUtil.isEmpty(userData) || !userData.getPassword().equals(SaSecureUtil.md5BySalt(user.getPassword(),"salt"))) { + throw new BadRequestException(LangProcess.msg("password_error")); + } + StpUtil.login(userData.getUser_id(),new SaLoginModel() + .setDevice("PC")); + Map data = new LinkedHashMap<>(); + data.put("user",userData); + data.put("token",StpUtil.getTokenValue()); + return SaResult.data(data); + } +} diff --git a/src/main/java/org/nl/apt15e/manage/user/dao/User.java b/src/main/java/org/nl/apt15e/manage/user/dao/User.java new file mode 100644 index 0000000..76ad6bf --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/user/dao/User.java @@ -0,0 +1,22 @@ +package org.nl.apt15e.manage.user.dao; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @author dsh + * 2025/10/23 + */ +@Data +@TableName("user") +public class User { + + @TableId(value = "user_id") + private String user_id; + + private String userName; + + private String password; + +} diff --git a/src/main/java/org/nl/apt15e/manage/user/service/UserService.java b/src/main/java/org/nl/apt15e/manage/user/service/UserService.java new file mode 100644 index 0000000..5f75754 --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/user/service/UserService.java @@ -0,0 +1,14 @@ +package org.nl.apt15e.manage.user.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.nl.apt15e.manage.secutiry.dto.AuthUserDto; +import org.nl.apt15e.manage.user.dao.User; +import org.springframework.stereotype.Service; + +/** + * @author dsh + * 2025/10/23 + */ +public interface UserService extends IService { + +} diff --git a/src/main/java/org/nl/apt15e/manage/user/service/impl/UserServiceImpl.java b/src/main/java/org/nl/apt15e/manage/user/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..b46c612 --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/user/service/impl/UserServiceImpl.java @@ -0,0 +1,17 @@ +package org.nl.apt15e.manage.user.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.nl.apt15e.manage.secutiry.dto.AuthUserDto; +import org.nl.apt15e.manage.user.dao.User; +import org.nl.apt15e.manage.user.service.UserService; +import org.nl.apt15e.manage.user.service.mapper.UserMapper; +import org.springframework.stereotype.Service; + +/** + * @author dsh + * 2025/10/23 + */ +@Service +public class UserServiceImpl extends ServiceImpl implements UserService { + +} diff --git a/src/main/java/org/nl/apt15e/manage/user/service/mapper/UserMapper.java b/src/main/java/org/nl/apt15e/manage/user/service/mapper/UserMapper.java new file mode 100644 index 0000000..acdf775 --- /dev/null +++ b/src/main/java/org/nl/apt15e/manage/user/service/mapper/UserMapper.java @@ -0,0 +1,14 @@ +package org.nl.apt15e.manage.user.service.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.nl.apt15e.manage.user.dao.User; + +/** + * @author dsh + * 2025/10/23 + */ +@Mapper +public interface UserMapper extends BaseMapper { + +} diff --git a/src/main/java/org/nl/apt15e/util/FileConstant.java b/src/main/java/org/nl/apt15e/util/FileConstant.java new file mode 100644 index 0000000..b684de0 --- /dev/null +++ b/src/main/java/org/nl/apt15e/util/FileConstant.java @@ -0,0 +1,27 @@ +package org.nl.apt15e.util; + +import org.nl.apt15e.config.file.FileProperties; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * @author dsh + * 2025/11/3 + **/ +@Component +public class FileConstant { + + + public static String ERROR_IMAGE_PATH = ""; + + @Resource + private FileProperties properties; + + @Value("${customize.errorImage-dir}") + public void setErrorImagePath(String errorImagePath) { + FileConstant.ERROR_IMAGE_PATH = properties.getPath().getPath()+errorImagePath; + } + +} diff --git a/src/main/java/org/nl/apt15e/util/MD5Util.java b/src/main/java/org/nl/apt15e/util/MD5Util.java new file mode 100644 index 0000000..64737ff --- /dev/null +++ b/src/main/java/org/nl/apt15e/util/MD5Util.java @@ -0,0 +1,20 @@ +package org.nl.apt15e.util; + +import cn.dev33.satoken.secure.SaSecureUtil; + +/** + * @author dsh + * 2025/10/23 + */ +public class MD5Util { + + public static void main(final String[] args) { + String str = "noblelift"; + + String salt = "salt"; + + String value = SaSecureUtil.md5BySalt(str,salt); + + System.out.println("加密后的值:"+value); + } +} diff --git a/src/main/java/org/nl/apt15e/util/RsaUtils.java b/src/main/java/org/nl/apt15e/util/RsaUtils.java new file mode 100644 index 0000000..7a4554b --- /dev/null +++ b/src/main/java/org/nl/apt15e/util/RsaUtils.java @@ -0,0 +1,90 @@ +package org.nl.apt15e.util; + +import org.apache.commons.codec.binary.Base64; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.Cipher; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; + +/** + * @author liejiu + */ +@Component +public class RsaUtils { + + public static String privateKey; + + @Value("${rsa.private_key}") + public void setPrivateKey(String privateKey) { + RsaUtils.privateKey = privateKey; + } + + public static void main(String[] args) throws Exception { + System.out.println("\n"); + RsaKeyPair keyPair = generateKeyPair(); + System.out.println("公钥:" + keyPair.getPublicKey()); + System.out.println("私钥:" + keyPair.getPrivateKey()); + } + + /** + * 构建RSA密钥对 + * + * @return / + * @throws NoSuchAlgorithmException / + */ + public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(1024); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); + String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded()); + String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded()); + return new RsaKeyPair(publicKeyString, privateKeyString); + } + + /** + * RSA密钥对对象 + */ + public static class RsaKeyPair { + + private final String publicKey; + private final String privateKey; + + public RsaKeyPair(String publicKey, String privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public String getPublicKey() { + return publicKey; + } + + public String getPrivateKey() { + return privateKey; + } + + } + + /** + * 私钥解密 + * + * @param privateKeyText 私钥 + * @param text 待解密的文本 + * @return / + * @throws Exception / + */ + public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception { + PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(Base64.decodeBase64(text)); + return new String(result); + } +} diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index b2cd20c..f486f02 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -66,4 +66,21 @@ file: avatar: C:\eladmin\avatar\ # 文件大小 /M maxSize: 100 - avatarMaxSize: 5 \ No newline at end of file + avatarMaxSize: 5 + +############## Sa-Token 配置 (文档: https://sa-token.cc) ############## +sa-token: + # token名称 (同时也是cookie名称) + token-name: Authorization + # token有效期,单位s 默认30天, -1代表永不过期 + timeout: 2592000 + # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 + activity-timeout: -1 + # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) + is-concurrent: true + # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) + is-share: false + # token风格 + token-style: random-128 + # 是否输出操作日志 + is-log: true \ No newline at end of file diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml index dd45a23..c83a39e 100644 --- a/src/main/resources/config/application-prod.yml +++ b/src/main/resources/config/application-prod.yml @@ -66,4 +66,21 @@ file: avatar: C:\eladmin\avatar\ # 文件大小 /M maxSize: 100 - avatarMaxSize: 5 \ No newline at end of file + avatarMaxSize: 5 + +############## Sa-Token 配置 (文档: https://sa-token.cc) ############## +sa-token: + # token名称 (同时也是cookie名称) + token-name: Authorization + # token有效期,单位s 默认30天, -1代表永不过期 + timeout: 2592000 + # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 + activity-timeout: -1 + # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) + is-concurrent: true + # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) + is-share: false + # token风格 + token-style: random-128 + # 是否输出操作日志 + is-log: true \ No newline at end of file diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml index 5c5d82e..0fe0ea6 100644 --- a/src/main/resources/config/application.yml +++ b/src/main/resources/config/application.yml @@ -24,4 +24,35 @@ mybatis-plus: logging: file: path: logs - config: classpath:logback-spring.xml \ No newline at end of file + config: classpath:logback-spring.xml + +customize: + log-dir: ${logging.file.path}/root + errorImage-dir: ErrorImage + +#密码加密传输,前端公钥加密,后端私钥解密 +rsa: + private_key: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMjhH5OmC7osPfdelXwkX1uTW1vuj8miZIU546Y6dy7TI2RF04rcW2eneBqrvF/8Ni1b+A+bqAJfMi01TDBmdyp+7vyZzUPRXv02HpI/ZM9dkQhv2m4VnTNnWOM0mY/7fJtDfLXhfDNmBMz7h57oDUFV0ESQtp5i4K7hlIVeezijAgMBAAECgYBVuPQfruksPnsHGB7UhjUHQD/pYEmN8zXQQJ7sLeD0Y3ej78RRaq268xVm1Eij8V4xRyD5kCRHNtaTwj3MHb3V68QC3amBR392yay4+S5ZOEPOjZ64hMSZWPezHbemLMjtUn1NR1k1aGfaiGPEk3npiXJHf9ZXU2GGglLJ50eP+QJBAPD3TTRtOVuTIPQS+m0PEeW6ALZDp3EONoqOMePuGLqj38QjwV+jGdxzA+Hhsf8oQo+QG2S3YPph09TFZc3dvj0CQQDVaY9TGeDyxf2DDL/oBFecIkixkV/AWRJb9CrDCZ8H9K1xWuEmhIW1pFJ+gmjbZccZQe/n1M+tRKq9DPaXsiBfAkANgE27GkOUdfHquwV9BtMh5AIWNEQ1eW5k5QK2mqiYDIaFHtu+2Ayi5W7aQSMQANl54cEnK38riD+uNEE3/6yhAkAN3ZfkTFAjNd3sv81QI8gVatzSPKG9+4uH0etdVKiyeaEzNjZerEmLrat2cL6jUo+HApO1ukvr9AQr2EXFQVt9AkEAoQcTx50AK6osp15kAoOQH5NV7TF2qoDbkNfp4xJReLlNpn9oPh2CNQOuVfna5gDQmkaEaJwwVuBvWN5RPrp2HA== + +# sa-token白名单配置 +security: + # 排除路径 + excludes: + # 认证 + - /auth/login + # apt屏幕操作 +# - /anomalyInfo/** + - /api/rcs/** + - /mapInfo/** + - /api/operate/** + - /teaching/** + - /vehicle/** + - /webSocket/** + - /file/** + - /routeInfo/** + - /station/** + # 静态资源 + - /*.html + - /**/*.html + - /**/*.css + - /**/*.js \ No newline at end of file diff --git a/src/main/resources/language/buss/buss.properties b/src/main/resources/language/buss/buss.properties index 0acf73e..8f76588 100644 --- a/src/main/resources/language/buss/buss.properties +++ b/src/main/resources/language/buss/buss.properties @@ -3,4 +3,5 @@ end_point = 终点 successful = 操作成功! failed = 操作失败! latest = 已经是最新的! -param_is_null = 参数为空! \ No newline at end of file +param_is_null = 参数为空! +password_error = 账号或密码错误 \ No newline at end of file diff --git a/src/main/resources/language/buss/buss_en_US.properties b/src/main/resources/language/buss/buss_en_US.properties index 64cfc46..d817150 100644 --- a/src/main/resources/language/buss/buss_en_US.properties +++ b/src/main/resources/language/buss/buss_en_US.properties @@ -3,4 +3,5 @@ end_point = End Point successful = successful! failed = Failed! latest = Already up to date! -param_is_null = The parameter is empty! \ No newline at end of file +param_is_null = The parameter is empty! +password_error = Wrong account or password \ No newline at end of file diff --git a/src/main/resources/language/buss/buss_zh_CN.properties b/src/main/resources/language/buss/buss_zh_CN.properties index 0acf73e..8f76588 100644 --- a/src/main/resources/language/buss/buss_zh_CN.properties +++ b/src/main/resources/language/buss/buss_zh_CN.properties @@ -3,4 +3,5 @@ end_point = 终点 successful = 操作成功! failed = 操作失败! latest = 已经是最新的! -param_is_null = 参数为空! \ No newline at end of file +param_is_null = 参数为空! +password_error = 账号或密码错误 \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 1a21870..493c4a4 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -24,11 +24,15 @@ - + - ${LOG_HOME}/root/info.%d{yyyy-MM-dd}.log - - 30 + ${LOG_HOME}/root/%d{yyyy-MM-dd}.%i.log + + 15 + + 20MB + + 1GB @@ -53,17 +57,17 @@ - - - - - + + 0 + 256 + + - + \ No newline at end of file