v3.0.0 【优化】登录日志回显、版本号改为3.0、文件下载

This commit is contained in:
zhuoda 2024-01-15 21:49:04 +08:00
parent 4142295ee1
commit 851d913ab6
25 changed files with 6058 additions and 104 deletions

View File

@ -4,7 +4,7 @@
<groupId>net.1024lab</groupId>
<artifactId>sa-parent</artifactId>
<version>1.0.0</version>
<version>3.0.0</version>
<packaging>pom</packaging>
<name>sa-parent</name>
@ -22,7 +22,7 @@
<springboot.version>2.7.5</springboot.version>
<spring-mock.version>2.0.8</spring-mock.version>
<mybatis-plus.version>3.5.2</mybatis-plus.version>
<p6spy.version>3.8.6</p6spy.version>
<p6spy.version>3.9.1</p6spy.version>
<springdoc-openapi-ui.version>1.7.0</springdoc-openapi-ui.version>
<knife4j.version>4.3.0</knife4j.version>
<fastjson.version>2.0.16</fastjson.version>
@ -50,6 +50,7 @@
<ip2region.version>2.7.0</ip2region.version>
<bcprov.version>1.59</bcprov.version>
<jackson-datatype-jsr310.version>2.13.4</jackson-datatype-jsr310.version>
<smartdb.version>1.2.0</smartdb.version>
</properties>
<dependencyManagement>
@ -293,12 +294,17 @@
<version>${jackson-datatype-jsr310.version}</version>
</dependency>
<dependency>
<groupId>net.1024lab</groupId>
<artifactId>smartdb</artifactId>
<version>${smartdb.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${profiles.active}-${project.name}</finalName>
<finalName>${project.name}-${profiles.active}-${project.version}</finalName>
<resources>
<resource>
<filtering>false</filtering>
@ -339,14 +345,6 @@
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>

View File

@ -4,12 +4,12 @@
<parent>
<groupId>net.1024lab</groupId>
<artifactId>sa-parent</artifactId>
<version>1.0.0</version>
<version>3.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>sa-admin</artifactId>
<version>1.0.0</version>
<version>3.0.0</version>
<packaging>jar</packaging>
<name>sa-admin</name>
@ -20,7 +20,7 @@
<dependency>
<groupId>net.1024lab</groupId>
<artifactId>sa-base</artifactId>
<version>1.0.0</version>
<version>3.0.0</version>
</dependency>
</dependencies>

View File

@ -37,5 +37,4 @@ public class AdminApplication {
application.addListeners(new LogVariableListener(), new Ip2RegionListener());
application.run(args);
}
}

View File

@ -159,6 +159,7 @@ public class AdminInterceptor implements HandlerInterceptor {
}
StpUtil.checkActiveTimeout();
StpUtil.updateLastActiveToNow();
}

View File

@ -17,7 +17,6 @@ import net.lab1024.sa.admin.module.system.menu.domain.vo.MenuVO;
import net.lab1024.sa.admin.module.system.role.domain.vo.RoleVO;
import net.lab1024.sa.admin.module.system.role.service.RoleEmployeeService;
import net.lab1024.sa.admin.module.system.role.service.RoleMenuService;
import net.lab1024.sa.base.common.code.ErrorCode;
import net.lab1024.sa.base.common.code.UserErrorCode;
import net.lab1024.sa.base.common.constant.RequestHeaderConst;
import net.lab1024.sa.base.common.constant.StringConst;
@ -37,8 +36,8 @@ import net.lab1024.sa.base.module.support.loginlog.LoginLogResultEnum;
import net.lab1024.sa.base.module.support.loginlog.LoginLogService;
import net.lab1024.sa.base.module.support.loginlog.domain.LoginLogEntity;
import net.lab1024.sa.base.module.support.loginlog.domain.LoginLogVO;
import net.lab1024.sa.base.module.support.securityprotect.service.ProtectLoginService;
import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailEntity;
import net.lab1024.sa.base.module.support.securityprotect.service.ProtectLoginService;
import net.lab1024.sa.base.module.support.securityprotect.service.ProtectPasswordService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@ -113,7 +112,6 @@ public class LoginService implements StpInterface {
/**
* 获取验证码
*
*/
public ResponseDTO<CaptchaVO> getCaptcha() {
return ResponseDTO.ok(captchaService.generateCaptcha());
@ -134,34 +132,46 @@ public class LoginService implements StpInterface {
// 校验 图形验证码
ResponseDTO<String> checkCaptcha = captchaService.checkCaptcha(loginForm);
if (!checkCaptcha.getOk()) {
return ResponseDTO.error(UserErrorCode.PARAM_ERROR,checkCaptcha.getMsg());
return ResponseDTO.error(UserErrorCode.PARAM_ERROR, checkCaptcha.getMsg());
}
// 验证账号和账号状态
// 验证登录名
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginForm.getLoginName());
if (null == employeeEntity) {
return ResponseDTO.userErrorParam("登录名不存在!");
}
// 验证账号状态
if (employeeEntity.getDisabledFlag()) {
saveLoginLog(employeeEntity, ip, userAgent, "账号已禁用", LoginLogResultEnum.LOGIN_FAIL);
return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!");
}
// 按照等保要求进行登录校验
// 解密前端加密的密码
String requestPassword = profectPasswordService.decryptPassword(loginForm.getPassword());
// 验证密码 是否为万能密码
String superPassword = configService.getConfigValue(ConfigKeyEnum.SUPER_PASSWORD);
boolean superPasswordFlag = superPassword.equals(requestPassword);
// 万能密码特殊操作
if (superPasswordFlag) {
// 对于万能密码受限制sa token 要求loginId唯一万能密码只能插入一段uuid
String saTokenLoginId = SUPER_PASSWORD_LOGIN_ID_PREFIX + StringConst.COLON + UUID.randomUUID().toString().replace("-", "") + StringConst.COLON + employeeEntity.getEmployeeId();
// 万能密码登录只能登录15分钟
StpUtil.login(saTokenLoginId, 900);
} else {
// 按照等保登录要求进行登录失败次数校验
ResponseDTO<LoginFailEntity> loginFailEntityResponseDTO = protectLoginService.checkLogin(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE);
if (!loginFailEntityResponseDTO.getOk()) {
return ResponseDTO.error(loginFailEntityResponseDTO);
}
// 解密前端加密的密码
loginForm.setPassword(profectPasswordService.decryptSm2Password(loginForm.getPassword()));
// 验证密码 1万能密码 或者 2真实密码
String superPassword = EmployeeService.getEncryptPwd(configService.getConfigValue(ConfigKeyEnum.SUPER_PASSWORD));
String requestPassword = EmployeeService.getEncryptPwd(loginForm.getPassword());
boolean superPasswordFlag = superPassword.equals(requestPassword);
if (!(superPasswordFlag || employeeEntity.getLoginPwd().equals(requestPassword))) {
// 密码错误
if (!employeeEntity.getLoginPwd().equals(EmployeeService.getEncryptPwd(requestPassword))) {
// 记录登录失败
saveLoginLog(employeeEntity, ip, userAgent, "密码错误", LoginLogResultEnum.LOGIN_FAIL);
// 记录等级保护次数
@ -169,16 +179,10 @@ public class LoginService implements StpInterface {
return msg == null ? ResponseDTO.userErrorParam("登录名或密码错误!") : ResponseDTO.error(UserErrorCode.LOGIN_FAIL_WILL_LOCK, msg);
}
// 生成 sa-token的 loginId对于万能密码受限制sa token 要求loginId唯一万能密码只能插入一段uuid
String saTokenLoginId = null;
if (superPasswordFlag) {
saTokenLoginId = SUPER_PASSWORD_LOGIN_ID_PREFIX + StringConst.COLON + UUID.randomUUID().toString().replace("-", "") + StringConst.COLON + employeeEntity.getEmployeeId();
} else {
saTokenLoginId = UserTypeEnum.ADMIN_EMPLOYEE.getValue() + StringConst.COLON + employeeEntity.getEmployeeId();
}
String saTokenLoginId = UserTypeEnum.ADMIN_EMPLOYEE.getValue() + StringConst.COLON + employeeEntity.getEmployeeId();
// 登录
StpUtil.login(saTokenLoginId, String.valueOf(loginDeviceEnum.getDesc()));
}
// 获取员工信息
RequestEmployee requestEmployee = loadLoginInfo(employeeEntity);
@ -186,15 +190,15 @@ public class LoginService implements StpInterface {
// 放入缓存
loginEmployeeCache.put(employeeEntity.getEmployeeId(), requestEmployee);
//保存登录记录
saveLoginLog(employeeEntity, ip, userAgent, superPasswordFlag ? "万能密码登录" : loginDeviceEnum.getDesc(), LoginLogResultEnum.LOGIN_SUCCESS);
// 移除登录失败
protectLoginService.removeLoginFail(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE);
// 获取登录结果信息
LoginResultVO loginResultVO = getLoginResult(requestEmployee);
//保存登录记录
saveLoginLog(employeeEntity, ip, userAgent, superPasswordFlag ? "万能密码登录" : loginDeviceEnum.getDesc(), LoginLogResultEnum.LOGIN_SUCCESS);
// 设置 token
loginResultVO.setToken(StpUtil.getTokenValue());
@ -207,11 +211,10 @@ public class LoginService implements StpInterface {
/**
* 获取登录结果信息
*
*/
public LoginResultVO getLoginResult(RequestEmployee requestEmployee) {
// 基础信息xde2
// 基础信息
LoginResultVO loginResultVO = SmartBeanUtil.copy(requestEmployee, LoginResultVO.class);
// 前端菜单和功能点清单
@ -224,7 +227,7 @@ public class LoginService implements StpInterface {
permissionCache.put(requestEmployee.getUserId(), userPermission);
// 上次登录信息
LoginLogVO loginLogVO = loginLogService.queryLastByUserId(requestEmployee.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE);
LoginLogVO loginLogVO = loginLogService.queryLastByUserId(requestEmployee.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE, LoginLogResultEnum.LOGIN_SUCCESS);
if (loginLogVO != null) {
loginResultVO.setLastLoginIp(loginLogVO.getLoginIp());
loginResultVO.setLastLoginIpRegion(loginLogVO.getLoginIpRegion());
@ -238,7 +241,6 @@ public class LoginService implements StpInterface {
/**
* 获取登录的用户信息
*
*/
private RequestEmployee loadLoginInfo(EmployeeEntity employeeEntity) {
@ -288,7 +290,6 @@ public class LoginService implements StpInterface {
/**
* 根据 loginId 获取 员工id
*
*/
Long getEmployeeIdByLoginId(String loginId) {
@ -315,7 +316,6 @@ public class LoginService implements StpInterface {
/**
* 退出登录
*
*/
public ResponseDTO<String> logout(String token, RequestUser requestUser) {
@ -343,15 +343,15 @@ public class LoginService implements StpInterface {
/**
* 保存登录日志
*
*/
private void saveLoginLog(EmployeeEntity employeeEntity, String ip, String userAgent, String remark, LoginLogResultEnum result) {
LoginLogEntity loginEntity = LoginLogEntity.builder()
.userId(employeeEntity.getEmployeeId())
.userType(UserTypeEnum.ADMIN_EMPLOYEE.getValue())
.userName(employeeEntity.getLoginName())
.userName(employeeEntity.getActualName())
.userAgent(userAgent)
.loginIp(ip)
.loginIpRegion(SmartIpUtil.getRegion(ip))
.remark(remark)
.loginResult(result.getValue())
.createTime(LocalDateTime.now())
@ -393,7 +393,6 @@ public class LoginService implements StpInterface {
/**
* 获取用户的权限包含 角色列表权限列表
*
*/
private UserPermission getUserPermission(Long employeeId) {

View File

@ -8,7 +8,7 @@
# 项目配置: 名称、日志目录
project:
name: sa-admin
log-directory: /home/smart-admin/${project.name}/${spring.profiles.active}
log-directory: /home/logs/smart_admin_v3/${project.name}/${spring.profiles.active}
# 项目端口和url根路径
server:

View File

@ -8,7 +8,7 @@
# 项目配置: 名称、日志目录
project:
name: sa-admin
log-directory: /home/smart-admin/${project.name}/${spring.profiles.active}
log-directory: /home/logs/smart_admin_v3/${project.name}/${spring.profiles.active}
# 项目端口和url根路径
server:

View File

@ -8,7 +8,7 @@
# 项目配置: 名称、日志目录
project:
name: sa-admin
log-directory: /home/smart-admin/${project.name}/${spring.profiles.active}
log-directory: /home/logs/smart_admin_v3/${project.name}/${spring.profiles.active}
# 项目端口和url根路径
server:

View File

@ -8,7 +8,7 @@
# 项目配置: 名称、日志目录
project:
name: sa-admin
log-directory: /home/smart-admin/${project.name}/${spring.profiles.active}
log-directory: /home/logs/smart_admin_v3/${project.name}/${spring.profiles.active}
# 项目端口和url根路径
server:

View File

@ -4,12 +4,12 @@
<parent>
<groupId>net.1024lab</groupId>
<artifactId>sa-parent</artifactId>
<version>1.0.0</version>
<version>3.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>sa-base</artifactId>
<version>1.0.0</version>
<version>3.0.0</version>
<name>sa-base</name>
<description>sa-base project</description>
@ -248,12 +248,17 @@
<artifactId>poi-scratchpad</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>net.1024lab</groupId>
<artifactId>smartdb</artifactId>
<version>${smartdb.version}</version>
</dependency>
</dependencies>

View File

@ -5,6 +5,7 @@ import net.lab1024.sa.base.common.constant.StringConst;
import org.lionsoul.ip2region.xdb.Searcher;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@ -46,21 +47,19 @@ public class SmartIpUtil {
* @return 返回结果例 [河南省, 洛阳市, 洛龙区]
*/
public static List<String> getRegionList(String ipStr) {
try {
List<String> regionList = new ArrayList<>();
try {
if (SmartStringUtil.isEmpty(ipStr)) {
return regionList;
}
ipStr = ipStr.trim();
String region = IP_SEARCHER.search(ipStr);
String[] split = region.split("\\|");
for (String str : split) {
regionList.add(str);
regionList.addAll(Arrays.asList(split));
} catch (Exception e) {
log.error("解析ip地址出错", e);
}
return regionList;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
@ -77,7 +76,8 @@ public class SmartIpUtil {
ipStr = ipStr.trim();
return IP_SEARCHER.search(ipStr);
} catch (Exception e) {
throw new RuntimeException(e);
log.error("解析ip地址出错", e);
return StringConst.EMPTY;
}
}

View File

@ -76,7 +76,8 @@ public class FileConfig implements WebMvcConfigurer {
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
.withClientConfiguration(clientConfig)
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withPathStyleAccessEnabled(true)
.withPathStyleAccessEnabled(false)
.withChunkedEncodingDisabled(true)
.build();
}

View File

@ -1,5 +1,7 @@
package net.lab1024.sa.base.module.support.file.service;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
@ -30,6 +32,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@ -84,7 +87,8 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
String fileType = FilenameUtils.getExtension(originalFileName);
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
String fileKey = path + uuid + "." + fileType;
String time = LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_FORMATTER);
String fileKey = path + uuid + "_" + time+ "." + fileType;
// 文件名称 URL 编码
String urlEncoderFilename;

View File

@ -40,7 +40,7 @@ public interface LoginLogDao extends BaseMapper<LoginLogEntity> {
* @param userType
* @return LoginLogVO
*/
LoginLogVO queryLastByUserId(@Param("userId") Long userId,@Param("userType") Integer userType);
LoginLogVO queryLastByUserId(@Param("userId") Long userId,@Param("userType") Integer userType, @Param("loginLogResult")Integer loginLogResult);
}

View File

@ -60,8 +60,8 @@ public class LoginLogService {
* @author 卓大
* @description 查询上一个登录记录
*/
public LoginLogVO queryLastByUserId(Long userId, UserTypeEnum userTypeEnum) {
return loginLogDao.queryLastByUserId(userId,userTypeEnum.getValue());
public LoginLogVO queryLastByUserId(Long userId, UserTypeEnum userTypeEnum, LoginLogResultEnum loginLogResultEnum) {
return loginLogDao.queryLastByUserId(userId,userTypeEnum.getValue(), loginLogResultEnum.getValue());
}
}

View File

@ -91,7 +91,7 @@ public class ProtectPasswordService {
* @param encryptedPassword
* @return
*/
public String decryptSm2Password(String encryptedPassword) {
public String decryptPassword(String encryptedPassword) {
return apiEncryptService.decrypt(encryptedPassword);
}

View File

@ -1,9 +1,9 @@
spring:
# 数据库连接信息
datasource:
url: jdbc:p6spy:mysql://127.0.0.1:3306/smart_admin_v3?autoReconnect=true&useServerPreparedStmts=false&rewriteBatchedStatements=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
url: jdbc:p6spy:mysql://127.0.0.1/smart_admin_v3?autoReconnect=true&useServerPreparedStmts=false&rewriteBatchedStatements=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: Java@1024
password: Zhuoda.666
initial-size: 2
min-idle: 2
max-active: 10
@ -65,6 +65,22 @@ server:
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
# 文件上传 配置
#file:
# storage:
# mode: local
# local:
# upload-path: /home/smart_admin_v3/upload/ #文件上传目录
# url-prefix:
# cloud:
# region: oss-cn-qingdao
# endpoint: oss-cn-qingdao.aliyuncs.com
# bucket-name: common
# access-key:
# secret-key:
# url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
# private-url-expire-seconds: 3600
# 文件上传 配置
file:
storage:
@ -73,15 +89,14 @@ file:
upload-path: /home/smart_admin_v3/upload/ #文件上传目录
url-prefix:
cloud:
region: oss-cn-qingdao
endpoint: oss-cn-qingdao.aliyuncs.com
bucket-name: common
region: oss-cn-hangzhou
endpoint: oss-cn-hangzhou.aliyuncs.com
bucket-name: smart-admin
access-key:
secret-key:
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
private-url-expire-seconds: 3600
# open api配置
springdoc:
swagger-ui:

View File

@ -30,7 +30,8 @@
where
user_id = #{userId}
and user_type = #{userType}
order by create_time desc
and login_result = #{loginLogResult}
order by login_log_id desc
limit 1
</select>

View File

@ -1,3 +1,3 @@
NODE_ENV=production
VITE_APP_TITLE='SmartAdmin 预发布环境(Pre)'
VITE_APP_API_URL='http://preview.smartadmin.1024lab.net/smart-admin-api'
VITE_APP_API_URL='https://preview.smartadmin.vip/smart-admin-api'

View File

@ -1,3 +1,3 @@
NODE_ENV=production
VITE_APP_TITLE='SmartAdmin V3.X'
VITE_APP_API_URL='http://preview.smartadmin.1024lab.net/smart-admin-api'
VITE_APP_API_URL='https://preview.smartadmin.vip/smart-admin-api'

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,6 @@ export const fileApi = {
* 下载文件流根据fileKey @author 胡克
*/
downLoadFile: (fileName, fileKey) => {
return getDownload(fileName, '/support/file/downLoad', { fileKey });
return getDownload('/support/file/downLoad', { fileKey });
},
};

View File

@ -62,7 +62,7 @@
setVisible(true);
return;
}
getDownload(fileItem.fileName, fileItem.fileUrl);
window.open(fileItem.fileUrl);
}
//

View File

@ -64,7 +64,7 @@
previewCurrent.value = index;
visible.value = true;
} else {
getDownload(file.fileName, file.fileUrl);
window.open(file.fileUrl);
}
}

View File

@ -140,7 +140,6 @@
{
title: '文件名称',
dataIndex: 'fileName',
ellipsis: true,
width: 200,
},
{
@ -152,7 +151,6 @@
{
title: '文件key',
dataIndex: 'fileKey',
ellipsis: true,
},
{
title: '文件类型',
@ -218,7 +216,7 @@
//
function onSearch(){
function onSearch() {
queryForm.pageNum = 1;
queryData();
}