diff --git a/smart-admin-api-java17-springboot3/pom.xml b/smart-admin-api-java17-springboot3/pom.xml index 4d8ad346..212f55df 100644 --- a/smart-admin-api-java17-springboot3/pom.xml +++ b/smart-admin-api-java17-springboot3/pom.xml @@ -21,6 +21,7 @@ UTF-8 3.3.1 2.0.8 + 6.4.3 3.5.7 3.9.1 4.4.0 @@ -48,7 +49,7 @@ 3.1 1.37.0 2.7.0 - 1.59 + 1.80 2.13.4 2.16.1 1.2.0 @@ -56,6 +57,7 @@ 2.2 2.3.33 1.18.1 + 3.1.0 @@ -83,6 +85,12 @@ + + org.springframework.security + spring-security-crypto + ${spring-security-crypto.version} + + p6spy p6spy @@ -235,7 +243,7 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on ${bcprov.version} @@ -318,6 +326,12 @@ ${freemarker.version} + + org.apache.tika + tika-core + ${tika.version} + + @@ -407,4 +421,4 @@ - \ No newline at end of file + diff --git a/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/domain/entity/DepartmentEntity.java b/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/domain/entity/DepartmentEntity.java index 52882f8d..3887b47f 100644 --- a/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/domain/entity/DepartmentEntity.java +++ b/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/domain/entity/DepartmentEntity.java @@ -3,6 +3,8 @@ package net.lab1024.sa.admin.module.system.department.domain.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.FieldStrategy; import lombok.Data; import java.time.LocalDateTime; @@ -34,6 +36,7 @@ public class DepartmentEntity { /** * 负责人员工 id */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) private Long managerId; /** diff --git a/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java b/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java index 959e474e..47c59b7e 100644 --- a/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java +++ b/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java @@ -297,38 +297,39 @@ public class EmployeeService { if (employeeEntity == null) { return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST); } + // 校验原始密码 - String oldPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getOldPassword()); - if (!Objects.equals(oldPassword, employeeEntity.getLoginPwd())) { + if (!SecurityPasswordService.matchesPwd(updatePasswordForm.getOldPassword(),employeeEntity.getLoginPwd()) ) { return ResponseDTO.userErrorParam("原密码有误,请重新输入"); } + // 新旧密码相同 + if (Objects.equals(updatePasswordForm.getOldPassword(), updatePasswordForm.getNewPassword()) ){ + return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入"); + } + // 校验密码复杂度 ResponseDTO validatePassComplexity = securityPasswordService.validatePasswordComplexity(updatePasswordForm.getNewPassword()); if (!validatePassComplexity.getOk()) { return validatePassComplexity; } - // 新旧密码相同 - String newPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword()); - if (Objects.equals(oldPassword, newPassword)) { - return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入"); - } - // 根据三级等保规则,校验密码是否重复 ResponseDTO passwordRepeatTimes = securityPasswordService.validatePasswordRepeatTimes(requestUser, updatePasswordForm.getNewPassword()); if (!passwordRepeatTimes.getOk()) { return ResponseDTO.error(passwordRepeatTimes); } + // 更新密码 + String newEncryptPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword()); EmployeeEntity updateEntity = new EmployeeEntity(); updateEntity.setEmployeeId(employeeId); - updateEntity.setLoginPwd(newPassword); + updateEntity.setLoginPwd(newEncryptPassword); employeeDao.updateById(updateEntity); // 保存修改密码密码记录 - securityPasswordService.saveUserChangePasswordLog(requestUser, newPassword, oldPassword); + securityPasswordService.saveUserChangePasswordLog(requestUser, newEncryptPassword, employeeEntity.getLoginPwd()); return ResponseDTO.ok(); } diff --git a/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java b/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java index 90c76eac..a4fb79a5 100644 --- a/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java +++ b/smart-admin-api-java17-springboot3/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java @@ -162,10 +162,15 @@ public class LoginService implements StpInterface { // 验证登录名 EmployeeEntity employeeEntity = employeeService.getByLoginName(loginForm.getLoginName()); if (null == employeeEntity) { - return ResponseDTO.userErrorParam("登录名不存在!"); + return ResponseDTO.userErrorParam("登录名或密码错误!"); } // 验证账号状态 + if (employeeEntity.getDeletedFlag()) { + saveLoginLog(employeeEntity, ip, userAgent, "账号已删除", LoginLogResultEnum.LOGIN_FAIL); + return ResponseDTO.userErrorParam("您的账号已被删除,请联系工作人员!"); + } + if (employeeEntity.getDisabledFlag()) { saveLoginLog(employeeEntity, ip, userAgent, "账号已禁用", LoginLogResultEnum.LOGIN_FAIL); return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!"); @@ -201,7 +206,7 @@ public class LoginService implements StpInterface { } // 密码错误 - if (!employeeEntity.getLoginPwd().equals(SecurityPasswordService.getEncryptPwd(requestPassword))) { + if ( !SecurityPasswordService.matchesPwd(requestPassword,employeeEntity.getLoginPwd()) ) { // 记录登录失败 saveLoginLog(employeeEntity, ip, userAgent, "密码错误", LoginLogResultEnum.LOGIN_FAIL); // 记录等级保护次数 @@ -504,10 +509,14 @@ public class LoginService implements StpInterface { // 验证登录名 EmployeeEntity employeeEntity = employeeService.getByLoginName(loginName); if (null == employeeEntity) { - return ResponseDTO.userErrorParam("登录名不存在!"); + return ResponseDTO.ok(); } // 验证账号状态 + if (employeeEntity.getDeletedFlag()) { + return ResponseDTO.userErrorParam("您的账号已被删除,请联系工作人员!"); + } + if (employeeEntity.getDisabledFlag()) { return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!"); } diff --git a/smart-admin-api-java17-springboot3/sa-base/pom.xml b/smart-admin-api-java17-springboot3/sa-base/pom.xml index fed5c0e9..97920440 100644 --- a/smart-admin-api-java17-springboot3/sa-base/pom.xml +++ b/smart-admin-api-java17-springboot3/sa-base/pom.xml @@ -49,6 +49,11 @@ + + org.springframework.security + spring-security-crypto + + cn.dev33 @@ -204,7 +209,7 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on @@ -268,7 +273,12 @@ freemarker + + org.apache.tika + tika-core + + - \ No newline at end of file + diff --git a/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java b/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java index 5f90b153..00cb51ed 100644 --- a/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java +++ b/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java @@ -161,6 +161,10 @@ public class Level3ProtectConfigService { this.maxUploadFileSizeMb = configForm.getMaxUploadFileSizeMb(); } + if (configForm.getLoginFailMaxTimes() != null) { + this.loginFailMaxTimes = configForm.getLoginFailMaxTimes(); + } + if (configForm.getLoginFailLockMinutes() != null) { this.loginFailLockSeconds = configForm.getLoginFailLockMinutes() * 60; } diff --git a/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java b/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java index 6774a43c..5a0117e3 100644 --- a/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java +++ b/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java @@ -2,10 +2,19 @@ package net.lab1024.sa.base.module.support.securityprotect.service; import jakarta.annotation.Resource; import net.lab1024.sa.base.common.domain.ResponseDTO; +import org.apache.tika.config.TikaConfig; +import org.apache.tika.exception.TikaException; +import org.apache.tika.io.TikaInputStream; +import org.apache.tika.metadata.Metadata; +import org.apache.tika.metadata.TikaCoreProperties; +import org.apache.tika.mime.MediaType; +import org.apache.tika.mime.MimeTypes; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; /** * 三级等保 文件上传 相关 @@ -23,6 +32,28 @@ public class SecurityFileService { @Resource private Level3ProtectConfigService level3ProtectConfigService; + // 定义白名单MIME类型 + private static final List ALLOWED_MIME_TYPES = Arrays.asList( + "application/json", + "application/zip", + "application/x-7z-compressed", + "application/pdf", + "application/vnd.ms-excel", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.ms-powerpoint", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.ms-works", + "text/csv", + "audio/*", + "video/*", + // 图片类型 svg有安全隐患,所以不使用"image/*" + "image/jpeg", + "image/png", + "image/gif", + "image/bmp" + ); /** * 检测文件安全类型 @@ -38,15 +69,50 @@ public class SecurityFileService { } // 文件类型安全检测 - if (!level3ProtectConfigService.isFileDetectFlag()) { - return ResponseDTO.ok(); + if (level3ProtectConfigService.isFileDetectFlag()) { + String fileType = getFileMimeType(file); + if(ALLOWED_MIME_TYPES.stream() + .noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){ + return ResponseDTO.userErrorParam("禁止上传此文件类型"); + } } - // 检测文件类型 - // ..... - return ResponseDTO.ok(); } + /** + * 获取文件的 MIME 类型 + * + * @param file 要检查的文件 + * @return 文件的 MIME 类型 + * + */ + public static String getFileMimeType(MultipartFile file) { + try { + TikaConfig tika = new TikaConfig(); + Metadata metadata = new Metadata(); + metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, file.getOriginalFilename()); + TikaInputStream stream = TikaInputStream.get(file.getInputStream()); + MediaType mimetype = tika.getDetector().detect(stream, metadata); + return mimetype.toString(); + } catch (IOException | TikaException e) { + return MimeTypes.OCTET_STREAM; + } + } + + /** + * 检查文件的 MIME 类型是否与指定的MIME 类型匹配(支持通配符) + * + * @param fileType 文件的 MIME 类型 + * @param mimetype MIME 类型(支持通配符) + * @return 是否匹配 + */ + private static boolean matchesMimeType(String fileType, String mimetype) { + if (mimetype.endsWith("/*")) { + String prefix = mimetype.substring(0, mimetype.length() - 1); + return fileType.startsWith(prefix); + } else { + return fileType.equalsIgnoreCase(mimetype); + } + } } -; \ No newline at end of file diff --git a/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java b/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java index d3a58c59..64732dd4 100644 --- a/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java +++ b/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java @@ -8,6 +8,7 @@ import net.lab1024.sa.base.module.support.securityprotect.dao.PasswordLogDao; import net.lab1024.sa.base.module.support.securityprotect.domain.PasswordLogEntity; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @@ -46,6 +47,8 @@ public class SecurityPasswordService { @Resource private Level3ProtectConfigService level3ProtectConfigService; + static Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8(); + /** * 校验密码复杂度 */ @@ -84,8 +87,9 @@ public class SecurityPasswordService { // 检查最近几次是否有重复密码 List oldPasswords = passwordLogDao.selectOldPassword(requestUser.getUserType().getValue(), requestUser.getUserId(), level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes()); - if (oldPasswords != null && oldPasswords.contains(getEncryptPwd(newPassword))) { - return ResponseDTO.userErrorParam(String.format("与前%s个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes())); + boolean isDuplicate = oldPasswords.stream().anyMatch(oldPassword -> encoder.matches(newPassword, oldPassword)); + if (isDuplicate) { + return ResponseDTO.userErrorParam(String.format("与前%d个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes())); } return ResponseDTO.ok(); @@ -143,7 +147,14 @@ public class SecurityPasswordService { * 获取 加密后 的密码 */ public static String getEncryptPwd(String password) { - return DigestUtils.md5Hex(String.format(PASSWORD_SALT_FORMAT, password)); + return encoder.encode(password); + } + + /** + * 校验密码是否匹配 + */ + public static Boolean matchesPwd( String password, String encodedPassword){ + return encoder.matches( password, encodedPassword); } } diff --git a/smart-admin-api-java8-springboot2/pom.xml b/smart-admin-api-java8-springboot2/pom.xml index dd21317f..f0567b0f 100644 --- a/smart-admin-api-java8-springboot2/pom.xml +++ b/smart-admin-api-java8-springboot2/pom.xml @@ -21,6 +21,7 @@ 1.8 2.7.18 2.0.8 + 5.8.16 3.5.2 8.0.33 3.9.1 @@ -50,7 +51,7 @@ 3.1 1.37.0 2.7.0 - 1.59 + 1.80 2.13.4 2.16.1 1.2.0 @@ -58,6 +59,7 @@ 2.2 2.3.33 1.18.1 + 2.9.3 @@ -85,6 +87,12 @@ + + org.springframework.security + spring-security-crypto + ${spring-security-crypto.version} + + com.mysql mysql-connector-j @@ -261,7 +269,7 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on ${bcprov.version} @@ -363,6 +371,12 @@ ${freemarker.version} + + org.apache.tika + tika-core + ${tika.version} + + @@ -451,4 +465,4 @@ - \ No newline at end of file + diff --git a/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/domain/entity/DepartmentEntity.java b/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/domain/entity/DepartmentEntity.java index 52882f8d..a76ea7e9 100644 --- a/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/domain/entity/DepartmentEntity.java +++ b/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/domain/entity/DepartmentEntity.java @@ -3,6 +3,8 @@ package net.lab1024.sa.admin.module.system.department.domain.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.FieldStrategy; import lombok.Data; import java.time.LocalDateTime; @@ -34,6 +36,7 @@ public class DepartmentEntity { /** * 负责人员工 id */ + @TableField(updateStrategy = FieldStrategy.IGNORED) private Long managerId; /** diff --git a/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java b/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java index c4e57e92..6df2df0c 100644 --- a/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java +++ b/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java @@ -297,38 +297,39 @@ public class EmployeeService { if (employeeEntity == null) { return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST); } + // 校验原始密码 - String oldPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getOldPassword()); - if (!Objects.equals(oldPassword, employeeEntity.getLoginPwd())) { + if (!SecurityPasswordService.matchesPwd(updatePasswordForm.getOldPassword(),employeeEntity.getLoginPwd()) ) { return ResponseDTO.userErrorParam("原密码有误,请重新输入"); } + // 新旧密码相同 + if (Objects.equals(updatePasswordForm.getOldPassword(), updatePasswordForm.getNewPassword()) ){ + return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入"); + } + // 校验密码复杂度 ResponseDTO validatePassComplexity = securityPasswordService.validatePasswordComplexity(updatePasswordForm.getNewPassword()); if (!validatePassComplexity.getOk()) { return validatePassComplexity; } - // 新旧密码相同 - String newPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword()); - if (Objects.equals(oldPassword, newPassword)) { - return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入"); - } - // 根据三级等保规则,校验密码是否重复 ResponseDTO passwordRepeatTimes = securityPasswordService.validatePasswordRepeatTimes(requestUser, updatePasswordForm.getNewPassword()); if (!passwordRepeatTimes.getOk()) { return ResponseDTO.error(passwordRepeatTimes); } + // 更新密码 + String newEncryptPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword()); EmployeeEntity updateEntity = new EmployeeEntity(); updateEntity.setEmployeeId(employeeId); - updateEntity.setLoginPwd(newPassword); + updateEntity.setLoginPwd(newEncryptPassword); employeeDao.updateById(updateEntity); // 保存修改密码密码记录 - securityPasswordService.saveUserChangePasswordLog(requestUser, newPassword, oldPassword); + securityPasswordService.saveUserChangePasswordLog(requestUser, newEncryptPassword, employeeEntity.getLoginPwd()); return ResponseDTO.ok(); } diff --git a/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java b/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java index 2f1bb2ad..efb82c66 100644 --- a/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java +++ b/smart-admin-api-java8-springboot2/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java @@ -164,10 +164,15 @@ public class LoginService implements StpInterface { // 验证登录名 EmployeeEntity employeeEntity = employeeService.getByLoginName(loginForm.getLoginName()); if (null == employeeEntity) { - return ResponseDTO.userErrorParam("登录名不存在!"); + return ResponseDTO.userErrorParam("登录名或密码错误!"); } // 验证账号状态 + if (employeeEntity.getDeletedFlag()) { + saveLoginLog(employeeEntity, ip, userAgent, "账号已删除", LoginLogResultEnum.LOGIN_FAIL); + return ResponseDTO.userErrorParam("您的账号已被删除,请联系工作人员!"); + } + if (employeeEntity.getDisabledFlag()) { saveLoginLog(employeeEntity, ip, userAgent, "账号已禁用", LoginLogResultEnum.LOGIN_FAIL); return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!"); @@ -203,7 +208,7 @@ public class LoginService implements StpInterface { } // 密码错误 - if (!employeeEntity.getLoginPwd().equals(SecurityPasswordService.getEncryptPwd(requestPassword))) { + if ( !SecurityPasswordService.matchesPwd(requestPassword,employeeEntity.getLoginPwd()) ) { // 记录登录失败 saveLoginLog(employeeEntity, ip, userAgent, "密码错误", LoginLogResultEnum.LOGIN_FAIL); // 记录等级保护次数 @@ -506,10 +511,14 @@ public class LoginService implements StpInterface { // 验证登录名 EmployeeEntity employeeEntity = employeeService.getByLoginName(loginName); if (null == employeeEntity) { - return ResponseDTO.userErrorParam("登录名不存在!"); + return ResponseDTO.ok(); } // 验证账号状态 + if (employeeEntity.getDeletedFlag()) { + return ResponseDTO.userErrorParam("您的账号已被删除,请联系工作人员!"); + } + if (employeeEntity.getDisabledFlag()) { return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!"); } diff --git a/smart-admin-api-java8-springboot2/sa-base/pom.xml b/smart-admin-api-java8-springboot2/sa-base/pom.xml index 52e773e1..b85edb17 100644 --- a/smart-admin-api-java8-springboot2/sa-base/pom.xml +++ b/smart-admin-api-java8-springboot2/sa-base/pom.xml @@ -87,6 +87,11 @@ spring-boot-starter-test + + org.springframework.security + spring-security-crypto + + com.mysql mysql-connector-j @@ -225,7 +230,7 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on @@ -298,8 +303,13 @@ freemarker + + org.apache.tika + tika-core + ${tika.version} + - \ No newline at end of file + diff --git a/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java b/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java index 343b868d..db573d2a 100644 --- a/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java +++ b/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java @@ -162,6 +162,10 @@ public class Level3ProtectConfigService { this.maxUploadFileSizeMb = configForm.getMaxUploadFileSizeMb(); } + if (configForm.getLoginFailMaxTimes() != null) { + this.loginFailMaxTimes = configForm.getLoginFailMaxTimes(); + } + if (configForm.getLoginFailLockMinutes() != null) { this.loginFailLockSeconds = configForm.getLoginFailLockMinutes() * 60; } diff --git a/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java b/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java index cdea03a4..cd1190a5 100644 --- a/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java +++ b/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java @@ -1,11 +1,23 @@ package net.lab1024.sa.base.module.support.securityprotect.service; import net.lab1024.sa.base.common.domain.ResponseDTO; +import org.apache.tika.config.TikaConfig; +import org.apache.tika.detect.Detector; +import org.apache.tika.exception.TikaException; +import org.apache.tika.io.TikaInputStream; +import org.apache.tika.metadata.Metadata; +import org.apache.tika.metadata.TikaCoreProperties; +import org.apache.tika.mime.MediaType; +import org.apache.tika.mime.MimeTypes; +import org.apache.tika.parser.AutoDetectParser; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; /** * 三级等保 文件上传 相关 @@ -23,6 +35,28 @@ public class SecurityFileService { @Resource private Level3ProtectConfigService level3ProtectConfigService; + // 定义白名单MIME类型 + private static final List ALLOWED_MIME_TYPES = Arrays.asList( + "application/json", + "application/zip", + "application/x-7z-compressed", + "application/pdf", + "application/vnd.ms-excel", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.ms-powerpoint", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.ms-works", + "text/csv", + "audio/*", + "video/*", + // 图片类型 svg有安全隐患,所以不使用"image/*" + "image/jpeg", + "image/png", + "image/gif", + "image/bmp" + ); /** * 检测文件安全类型 @@ -38,15 +72,50 @@ public class SecurityFileService { } // 文件类型安全检测 - if (!level3ProtectConfigService.isFileDetectFlag()) { - return ResponseDTO.ok(); + if (level3ProtectConfigService.isFileDetectFlag()) { + String fileType = getFileMimeType(file); + if(ALLOWED_MIME_TYPES.stream() + .noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){ + return ResponseDTO.userErrorParam("禁止上传此文件类型"); + } } - // 检测文件类型 - // ..... - return ResponseDTO.ok(); } + /** + * 获取文件的 MIME 类型 + * + * @param file 要检查的文件 + * @return 文件的 MIME 类型 + * + */ + public static String getFileMimeType(MultipartFile file) { + try { + TikaConfig tika = new TikaConfig(); + Metadata metadata = new Metadata(); + metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, file.getOriginalFilename()); + TikaInputStream stream = TikaInputStream.get(file.getInputStream()); + MediaType mimetype = tika.getDetector().detect(stream, metadata); + return mimetype.toString(); + } catch (IOException | TikaException e) { + return MimeTypes.OCTET_STREAM; + } + } + + /** + * 检查文件的 MIME 类型是否与指定的MIME 类型匹配(支持通配符) + * + * @param fileType 文件的 MIME 类型 + * @param mimetype MIME 类型(支持通配符) + * @return 是否匹配 + */ + private static boolean matchesMimeType(String fileType, String mimetype) { + if (mimetype.endsWith("/*")) { + String prefix = mimetype.substring(0, mimetype.length() - 1); + return fileType.startsWith(prefix); + } else { + return fileType.equalsIgnoreCase(mimetype); + } + } } -; \ No newline at end of file diff --git a/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java b/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java index 3d371b44..98209315 100644 --- a/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java +++ b/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java @@ -5,8 +5,8 @@ import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.util.SmartStringUtil; import net.lab1024.sa.base.module.support.securityprotect.dao.PasswordLogDao; import net.lab1024.sa.base.module.support.securityprotect.domain.PasswordLogEntity; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -46,6 +46,8 @@ public class SecurityPasswordService { @Resource private Level3ProtectConfigService level3ProtectConfigService; + static Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8(); + /** * 校验密码复杂度 */ @@ -84,8 +86,9 @@ public class SecurityPasswordService { // 检查最近几次是否有重复密码 List oldPasswords = passwordLogDao.selectOldPassword(requestUser.getUserType().getValue(), requestUser.getUserId(), level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes()); - if (oldPasswords != null && oldPasswords.contains(getEncryptPwd(newPassword))) { - return ResponseDTO.userErrorParam(String.format("与前%s个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes())); + boolean isDuplicate = oldPasswords.stream().anyMatch(oldPassword -> encoder.matches(newPassword, oldPassword)); + if (isDuplicate) { + return ResponseDTO.userErrorParam(String.format("与前%d个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes())); } return ResponseDTO.ok(); @@ -143,7 +146,14 @@ public class SecurityPasswordService { * 获取 加密后 的密码 */ public static String getEncryptPwd(String password) { - return DigestUtils.md5Hex(String.format(PASSWORD_SALT_FORMAT, password)); + return encoder.encode(password); + } + + /** + * 校验密码是否匹配 + */ + public static Boolean matchesPwd( String password, String encodedPassword){ + return encoder.matches( password, encodedPassword); } } diff --git a/smart-admin-web-javascript/src/views/system/department/components/department-form-modal.vue b/smart-admin-web-javascript/src/views/system/department/components/department-form-modal.vue index c2a245c6..1abb2be7 100644 --- a/smart-admin-web-javascript/src/views/system/department/components/department-form-modal.vue +++ b/smart-admin-web-javascript/src/views/system/department/components/department-form-modal.vue @@ -11,128 +11,129 @@ - + - + - + diff --git a/smart-admin-web-typescript/src/views/system/department/components/department-form-modal.vue b/smart-admin-web-typescript/src/views/system/department/components/department-form-modal.vue index f11bee44..0f56a68a 100644 --- a/smart-admin-web-typescript/src/views/system/department/components/department-form-modal.vue +++ b/smart-admin-web-typescript/src/views/system/department/components/department-form-modal.vue @@ -11,128 +11,129 @@ - + - + - + diff --git a/smart_admin_v3.sql b/smart_admin_v3.sql index a5502635..44ecbe08 100644 --- a/smart_admin_v3.sql +++ b/smart_admin_v3.sql @@ -238,7 +238,7 @@ DROP TABLE IF EXISTS `t_employee`; CREATE TABLE `t_employee` ( `employee_id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键', `login_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '登录帐号', - `login_pwd` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '登录密码', + `login_pwd` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '登录密码', `actual_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '员工名称', `avatar` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `gender` tinyint(1) NOT NULL DEFAULT 0 COMMENT '性别', @@ -258,20 +258,20 @@ CREATE TABLE `t_employee` ( -- ---------------------------- -- Records of t_employee -- ---------------------------- -INSERT INTO `t_employee` VALUES (1, 'admin', '40cc20b8891cd3fd1f008ea7f4ac17c3', '管理员', 'public/common/1eea469452484ffea4a42570c4072466_20240702220447.jpg', 0, '13500000000', 1, 3, NULL, 0, 0, 1, NULL, '2024-09-03 21:39:17', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (2, 'huke', '40cc20b8891cd3fd1f008ea7f4ac17c3', '胡克', NULL, 0, '13123123121', 1, 4, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:09', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (44, 'zhuoda', 'bf63cb6431d613acdee104f692845b22', '卓大', NULL, 1, '18637925892', 1, 6, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:10', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (47, 'shanyi', 'ca405fddcb90ac2a71b33fe7126ed2a8', '善逸', 'public/common/f823b00873684f0a9d31f0d62316cc8e_20240630015141.jpg', 1, '17630506613', 2, 5, NULL, 0, 0, 0, '这个是备注', '2024-09-03 21:36:11', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (48, 'qinjiu', 'b1cfb0ed0080306199fa76c872d6a32e', '琴酒', NULL, 2, '14112343212', 2, 6, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:12', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (63, 'kaiyun', '0e5ec5746bf955f253fa747ab76cfa67', '开云', NULL, 0, '13112312346', 2, 5, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:13', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (64, 'qingye', '40cc20b8891cd3fd1f008ea7f4ac17c3', '清野', NULL, 1, '13123123111', 2, 4, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:14', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (65, 'feiye', '40cc20b8891cd3fd1f008ea7f4ac17c3', '飞叶', NULL, 1, '13123123112', 4, 3, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:14', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (66, 'luoyi', '40cc20b8891cd3fd1f008ea7f4ac17c3', '罗伊', NULL, 1, '13123123142', 4, 2, NULL, 1, 0, 0, NULL, '2024-09-03 21:36:15', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (67, 'chuxiao', '7287168489ed5598741362cbec2b0741', '初晓', NULL, 1, '13123123123', 1, 2, NULL, 1, 0, 0, NULL, '2024-09-03 21:36:18', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (68, 'xuanpeng', '40cc20b8891cd3fd1f008ea7f4ac17c3', '玄朋', NULL, 1, '13123123124', 1, 3, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:18', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (69, 'peixian', '40cc20b8891cd3fd1f008ea7f4ac17c3', '玄朋', NULL, 1, '18377482773', 1, 4, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:19', '2022-10-04 21:33:50'); -INSERT INTO `t_employee` VALUES (73, 'limbo', '50ea4174e4ad0970bcf6423f99c0cbcd', '陈琳博', NULL, 1, '18906662339', 2, 4, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:24', '2024-07-17 10:36:16'); -INSERT INTO `t_employee` VALUES (74, 'xzh', 'f5ca8e50d26e6070ed2198e136ee967d', 'admin1', NULL, 1, '13654567897', 5, 6, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:21', '2024-08-09 09:49:56'); +INSERT INTO `t_employee` VALUES (1, 'admin', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '管理员', 'public/common/1eea469452484ffea4a42570c4072466_20240702220447.jpg', 0, '13500000000', 1, 3, NULL, 0, 0, 1, NULL, '2024-09-03 21:39:17', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (2, 'huke', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '胡克', NULL, 0, '13123123121', 1, 4, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:09', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (44, 'zhuoda', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '卓大', NULL, 1, '18637925892', 1, 6, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:10', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (47, 'shanyi', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '善逸', 'public/common/f823b00873684f0a9d31f0d62316cc8e_20240630015141.jpg', 1, '17630506613', 2, 5, NULL, 0, 0, 0, '这个是备注', '2024-09-03 21:36:11', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (48, 'qinjiu', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '琴酒', NULL, 2, '14112343212', 2, 6, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:12', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (63, 'kaiyun', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '开云', NULL, 0, '13112312346', 2, 5, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:13', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (64, 'qingye', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '清野', NULL, 1, '13123123111', 2, 4, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:14', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (65, 'feiye', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '飞叶', NULL, 1, '13123123112', 4, 3, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:14', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (66, 'luoyi', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '罗伊', NULL, 1, '13123123142', 4, 2, NULL, 1, 0, 0, NULL, '2024-09-03 21:36:15', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (67, 'chuxiao', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '初晓', NULL, 1, '13123123123', 1, 2, NULL, 1, 0, 0, NULL, '2024-09-03 21:36:18', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (68, 'xuanpeng', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '玄朋', NULL, 1, '13123123124', 1, 3, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:18', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (69, 'peixian', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '玄朋', NULL, 1, '18377482773', 1, 4, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:19', '2022-10-04 21:33:50'); +INSERT INTO `t_employee` VALUES (73, 'limbo', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', '陈琳博', NULL, 1, '18906662339', 2, 4, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:24', '2024-07-17 10:36:16'); +INSERT INTO `t_employee` VALUES (74, 'xzh', '$argon2id$v=19$m=16384,t=2,p=1$e/hqRAZYCYHydMS3SPo7yA$5hdCxLG7q+Jtf6KLJHVg/yb0I8LZrPuKUF66jLq+Drc', 'admin1', NULL, 1, '13654567897', 5, 6, NULL, 0, 0, 0, NULL, '2024-09-03 21:36:21', '2024-08-09 09:49:56'); -- ---------------------------- -- Table structure for t_feedback