opt:1.增加后端管理功能。2.优化系统日志。

This commit is contained in:
2025-11-07 16:40:12 +08:00
parent 71f2fd8da5
commit 27994afa9b
30 changed files with 773 additions and 48 deletions

View File

@@ -66,6 +66,13 @@
<version>3.5.2</version>
</dependency>
<!-- Sa-Token 权限认证 安全框架, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.34.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -68,7 +68,7 @@ public class MapInfoServiceImpl extends ServiceImpl<MapInfoMapper, MapInfo> impl
}
} catch (Exception e) {
log.info("同步地图失败:{}",e.getMessage());
throw new BadRequestException(LangProcess.msg("failed"));
throw new BadRequestException(LangProcess.msg("failed")+":"+e.getMessage());
}
}
}

View File

@@ -49,7 +49,7 @@ public class RouteInfo {
private Double end_y;
/**
* 导航模式 前进模式, 后退模式 ,无头模式
* 导航模式 前进模式, 后退模式 ,双向模式
*/
private String navigation_mode;

View File

@@ -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")

View File

@@ -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<RouteInfo> 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<RouteInfo> mergeBidirectionalRoutes(List<RouteInfo> originalData) {
List<RouteInfo> mergedData = new ArrayList<>();
Map<String, RouteInfo> routeMap = new HashMap<>();
Set<Integer> 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;
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -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<Object> getLogFiles() {
try {
return new ResponseEntity<>(logService.getLogFiles(), HttpStatus.OK);
} catch (Exception e) {
throw new BadRequestException("获取日志文件失败:"+e.getMessage());
}
}
/**
* 下载日志文件
*/
@Log("下载日志文件")
@GetMapping("/download/{fileName}")
public ResponseEntity<Resource> 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();
}
}
}

View File

@@ -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]);
}
}

View File

@@ -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<LogFileDTO> getLogFiles() {
List<LogFileDTO> 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;
}
}

View File

@@ -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<Object> login(@RequestBody AuthUserDto user) {
if (ObjectUtil.isEmpty(user)) {
throw new BadRequestException(LangProcess.msg("param_is_null"));
}
return new ResponseEntity<>(authorizationService.login(user), HttpStatus.OK);
}
}

View File

@@ -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;
}

View File

@@ -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<String, Object> login(AuthUserDto user){
// 密码解密 - 前端的加密规则: encrypt根据实际更改
String password = RsaUtils.decryptByPrivateKey(RsaUtils.privateKey, user.getPassword());
user.setPassword(password);
User userData = userService.getOne(new LambdaQueryWrapper<User>().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<String, Object> data = new LinkedHashMap<>();
data.put("user",userData);
data.put("token",StpUtil.getTokenValue());
return SaResult.data(data);
}
}

View File

@@ -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;
}

View File

@@ -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<User> {
}

View File

@@ -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<UserMapper, User> implements UserService {
}

View File

@@ -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<User> {
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -67,3 +67,20 @@ file:
# 文件大小 /M
maxSize: 100
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

View File

@@ -67,3 +67,20 @@ file:
# 文件大小 /M
maxSize: 100
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

View File

@@ -25,3 +25,34 @@ logging:
file:
path: logs
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

View File

@@ -4,3 +4,4 @@ successful = 操作成功!
failed = 操作失败!
latest = 已经是最新的!
param_is_null = 参数为空!
password_error = 账号或密码错误

View File

@@ -4,3 +4,4 @@ successful = successful!
failed = Failed!
latest = Already up to date!
param_is_null = The parameter is empty!
password_error = Wrong account or password

View File

@@ -4,3 +4,4 @@ successful = 操作成功!
failed = 操作失败!
latest = 已经是最新的!
param_is_null = 参数为空!
password_error = 账号或密码错误

View File

@@ -24,11 +24,15 @@
<!-- 日志首次输出的文件地址 -->
<!-- <file>${LOG_HOME}/info.log</file>-->
<!-- 滚动输出策略:基于时间创建日志文件 ,这样第二天输出的日志,就会按照 fileNamePattern 新建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${LOG_HOME}/root/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>30</maxHistory>
<fileNamePattern>${LOG_HOME}/root/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
<!--单个日志最大容量 至少10MB才能看得出来-->
<maxFileSize>20MB</maxFileSize>
<!--所有日志最多占多大容量-->
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<!-- 日志内容输出格式设置为定义好的 log.pattern-->
@@ -53,17 +57,17 @@
<!-- <appender-ref ref="console"/>-->
<!-- </appender>-->
<!-- <appender name="async_file_info" class="ch.qos.logback.classic.AsyncAppender">-->
<!-- <discardingThreshold>0</discardingThreshold>-->
<!-- <queueSize>256</queueSize>-->
<!-- <appender-ref ref="file_info"/>-->
<!-- </appender>-->
<appender name="async_file_info" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="file_info"/>
</appender>
<!--系统操作日志 root 根路径的日志级别 info -->
<root level="info">
<!-- 将定义好的几个日志输出 追加到 root 上 -->
<!-- <appender-ref ref="console"/>-->
<appender-ref ref="console" />
<!-- <appender-ref ref="async_file_info" />-->
<appender-ref ref="async_file_info" />
</root>
</configuration>