mirror of
https://gitee.com/lab1024/smart-admin.git
synced 2025-09-18 03:16:40 +08:00
!58 修复:登录失败锁定不生效 、账号删除后依然能登录、用户名枚举漏洞
Merge pull request !58 from 憨涛子/master
This commit is contained in:
commit
ad7ad667c8
@ -21,6 +21,7 @@
|
|||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<springboot.version>3.3.1</springboot.version>
|
<springboot.version>3.3.1</springboot.version>
|
||||||
<spring-mock.version>2.0.8</spring-mock.version>
|
<spring-mock.version>2.0.8</spring-mock.version>
|
||||||
|
<spring-security-crypto.version>6.4.3</spring-security-crypto.version>
|
||||||
<mybatis-plus.version>3.5.7</mybatis-plus.version>
|
<mybatis-plus.version>3.5.7</mybatis-plus.version>
|
||||||
<p6spy.version>3.9.1</p6spy.version>
|
<p6spy.version>3.9.1</p6spy.version>
|
||||||
<knife4j.version>4.4.0</knife4j.version>
|
<knife4j.version>4.4.0</knife4j.version>
|
||||||
@ -48,7 +49,7 @@
|
|||||||
<velocity-tools.version>3.1</velocity-tools.version>
|
<velocity-tools.version>3.1</velocity-tools.version>
|
||||||
<sa-token.version>1.37.0</sa-token.version>
|
<sa-token.version>1.37.0</sa-token.version>
|
||||||
<ip2region.version>2.7.0</ip2region.version>
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
<bcprov.version>1.59</bcprov.version>
|
<bcprov.version>1.80</bcprov.version>
|
||||||
<jackson-datatype-jsr310.version>2.13.4</jackson-datatype-jsr310.version>
|
<jackson-datatype-jsr310.version>2.13.4</jackson-datatype-jsr310.version>
|
||||||
<jackson-dataformat-yaml.version>2.16.1</jackson-dataformat-yaml.version>
|
<jackson-dataformat-yaml.version>2.16.1</jackson-dataformat-yaml.version>
|
||||||
<smartdb.version>1.2.0</smartdb.version>
|
<smartdb.version>1.2.0</smartdb.version>
|
||||||
@ -56,6 +57,7 @@
|
|||||||
<snakeyaml.version>2.2</snakeyaml.version>
|
<snakeyaml.version>2.2</snakeyaml.version>
|
||||||
<freemarker.version>2.3.33</freemarker.version>
|
<freemarker.version>2.3.33</freemarker.version>
|
||||||
<jsoup.version>1.18.1</jsoup.version>
|
<jsoup.version>1.18.1</jsoup.version>
|
||||||
|
<tika.version>3.1.0</tika.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -83,6 +85,12 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-crypto</artifactId>
|
||||||
|
<version>${spring-security-crypto.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>p6spy</groupId>
|
<groupId>p6spy</groupId>
|
||||||
<artifactId>p6spy</artifactId>
|
<artifactId>p6spy</artifactId>
|
||||||
@ -235,7 +243,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk15on</artifactId>
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
<version>${bcprov.version}</version>
|
<version>${bcprov.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
@ -318,6 +326,12 @@
|
|||||||
<version>${freemarker.version}</version>
|
<version>${freemarker.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>${tika.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
@ -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.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -34,6 +36,7 @@ public class DepartmentEntity {
|
|||||||
/**
|
/**
|
||||||
* 负责人员工 id
|
* 负责人员工 id
|
||||||
*/
|
*/
|
||||||
|
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||||
private Long managerId;
|
private Long managerId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -297,38 +297,39 @@ public class EmployeeService {
|
|||||||
if (employeeEntity == null) {
|
if (employeeEntity == null) {
|
||||||
return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST);
|
return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验原始密码
|
// 校验原始密码
|
||||||
String oldPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getOldPassword());
|
if (!SecurityPasswordService.matchesPwd(updatePasswordForm.getOldPassword(),employeeEntity.getLoginPwd()) ) {
|
||||||
if (!Objects.equals(oldPassword, employeeEntity.getLoginPwd())) {
|
|
||||||
return ResponseDTO.userErrorParam("原密码有误,请重新输入");
|
return ResponseDTO.userErrorParam("原密码有误,请重新输入");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新旧密码相同
|
||||||
|
if (Objects.equals(updatePasswordForm.getOldPassword(), updatePasswordForm.getNewPassword()) ){
|
||||||
|
return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入");
|
||||||
|
}
|
||||||
|
|
||||||
// 校验密码复杂度
|
// 校验密码复杂度
|
||||||
ResponseDTO<String> validatePassComplexity = securityPasswordService.validatePasswordComplexity(updatePasswordForm.getNewPassword());
|
ResponseDTO<String> validatePassComplexity = securityPasswordService.validatePasswordComplexity(updatePasswordForm.getNewPassword());
|
||||||
if (!validatePassComplexity.getOk()) {
|
if (!validatePassComplexity.getOk()) {
|
||||||
return validatePassComplexity;
|
return validatePassComplexity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新旧密码相同
|
|
||||||
String newPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword());
|
|
||||||
if (Objects.equals(oldPassword, newPassword)) {
|
|
||||||
return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据三级等保规则,校验密码是否重复
|
// 根据三级等保规则,校验密码是否重复
|
||||||
ResponseDTO<String> passwordRepeatTimes = securityPasswordService.validatePasswordRepeatTimes(requestUser, updatePasswordForm.getNewPassword());
|
ResponseDTO<String> passwordRepeatTimes = securityPasswordService.validatePasswordRepeatTimes(requestUser, updatePasswordForm.getNewPassword());
|
||||||
if (!passwordRepeatTimes.getOk()) {
|
if (!passwordRepeatTimes.getOk()) {
|
||||||
return ResponseDTO.error(passwordRepeatTimes);
|
return ResponseDTO.error(passwordRepeatTimes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 更新密码
|
// 更新密码
|
||||||
|
String newEncryptPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword());
|
||||||
EmployeeEntity updateEntity = new EmployeeEntity();
|
EmployeeEntity updateEntity = new EmployeeEntity();
|
||||||
updateEntity.setEmployeeId(employeeId);
|
updateEntity.setEmployeeId(employeeId);
|
||||||
updateEntity.setLoginPwd(newPassword);
|
updateEntity.setLoginPwd(newEncryptPassword);
|
||||||
employeeDao.updateById(updateEntity);
|
employeeDao.updateById(updateEntity);
|
||||||
|
|
||||||
// 保存修改密码密码记录
|
// 保存修改密码密码记录
|
||||||
securityPasswordService.saveUserChangePasswordLog(requestUser, newPassword, oldPassword);
|
securityPasswordService.saveUserChangePasswordLog(requestUser, newEncryptPassword, employeeEntity.getLoginPwd());
|
||||||
|
|
||||||
return ResponseDTO.ok();
|
return ResponseDTO.ok();
|
||||||
}
|
}
|
||||||
|
@ -162,10 +162,15 @@ public class LoginService implements StpInterface {
|
|||||||
// 验证登录名
|
// 验证登录名
|
||||||
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginForm.getLoginName());
|
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginForm.getLoginName());
|
||||||
if (null == employeeEntity) {
|
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()) {
|
if (employeeEntity.getDisabledFlag()) {
|
||||||
saveLoginLog(employeeEntity, ip, userAgent, "账号已禁用", LoginLogResultEnum.LOGIN_FAIL);
|
saveLoginLog(employeeEntity, ip, userAgent, "账号已禁用", LoginLogResultEnum.LOGIN_FAIL);
|
||||||
return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!");
|
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);
|
saveLoginLog(employeeEntity, ip, userAgent, "密码错误", LoginLogResultEnum.LOGIN_FAIL);
|
||||||
// 记录等级保护次数
|
// 记录等级保护次数
|
||||||
@ -504,10 +509,14 @@ public class LoginService implements StpInterface {
|
|||||||
// 验证登录名
|
// 验证登录名
|
||||||
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginName);
|
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginName);
|
||||||
if (null == employeeEntity) {
|
if (null == employeeEntity) {
|
||||||
return ResponseDTO.userErrorParam("登录名不存在!");
|
return ResponseDTO.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证账号状态
|
// 验证账号状态
|
||||||
|
if (employeeEntity.getDeletedFlag()) {
|
||||||
|
return ResponseDTO.userErrorParam("您的账号已被删除,请联系工作人员!");
|
||||||
|
}
|
||||||
|
|
||||||
if (employeeEntity.getDisabledFlag()) {
|
if (employeeEntity.getDisabledFlag()) {
|
||||||
return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!");
|
return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!");
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,11 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-crypto</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- sa-token start -->
|
<!-- sa-token start -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.dev33</groupId>
|
<groupId>cn.dev33</groupId>
|
||||||
@ -204,7 +209,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk15on</artifactId>
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -268,6 +273,11 @@
|
|||||||
<artifactId>freemarker</artifactId>
|
<artifactId>freemarker</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,6 +161,10 @@ public class Level3ProtectConfigService {
|
|||||||
this.maxUploadFileSizeMb = configForm.getMaxUploadFileSizeMb();
|
this.maxUploadFileSizeMb = configForm.getMaxUploadFileSizeMb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configForm.getLoginFailMaxTimes() != null) {
|
||||||
|
this.loginFailMaxTimes = configForm.getLoginFailMaxTimes();
|
||||||
|
}
|
||||||
|
|
||||||
if (configForm.getLoginFailLockMinutes() != null) {
|
if (configForm.getLoginFailLockMinutes() != null) {
|
||||||
this.loginFailLockSeconds = configForm.getLoginFailLockMinutes() * 60;
|
this.loginFailLockSeconds = configForm.getLoginFailLockMinutes() * 60;
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,19 @@ package net.lab1024.sa.base.module.support.securityprotect.service;
|
|||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
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.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
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
|
@Resource
|
||||||
private Level3ProtectConfigService level3ProtectConfigService;
|
private Level3ProtectConfigService level3ProtectConfigService;
|
||||||
|
|
||||||
|
// 定义白名单MIME类型
|
||||||
|
private static final List<String> 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()) {
|
if (level3ProtectConfigService.isFileDetectFlag()) {
|
||||||
return ResponseDTO.ok();
|
String fileType = getFileMimeType(file);
|
||||||
|
if(ALLOWED_MIME_TYPES.stream()
|
||||||
|
.noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){
|
||||||
|
return ResponseDTO.userErrorParam("禁止上传此文件类型");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测文件类型
|
|
||||||
// .....
|
|
||||||
|
|
||||||
return ResponseDTO.ok();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;
|
|
@ -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 net.lab1024.sa.base.module.support.securityprotect.domain.PasswordLogEntity;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -46,6 +47,8 @@ public class SecurityPasswordService {
|
|||||||
@Resource
|
@Resource
|
||||||
private Level3ProtectConfigService level3ProtectConfigService;
|
private Level3ProtectConfigService level3ProtectConfigService;
|
||||||
|
|
||||||
|
static Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验密码复杂度
|
* 校验密码复杂度
|
||||||
*/
|
*/
|
||||||
@ -84,8 +87,9 @@ public class SecurityPasswordService {
|
|||||||
|
|
||||||
// 检查最近几次是否有重复密码
|
// 检查最近几次是否有重复密码
|
||||||
List<String> oldPasswords = passwordLogDao.selectOldPassword(requestUser.getUserType().getValue(), requestUser.getUserId(), level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes());
|
List<String> oldPasswords = passwordLogDao.selectOldPassword(requestUser.getUserType().getValue(), requestUser.getUserId(), level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes());
|
||||||
if (oldPasswords != null && oldPasswords.contains(getEncryptPwd(newPassword))) {
|
boolean isDuplicate = oldPasswords.stream().anyMatch(oldPassword -> encoder.matches(newPassword, oldPassword));
|
||||||
return ResponseDTO.userErrorParam(String.format("与前%s个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes()));
|
if (isDuplicate) {
|
||||||
|
return ResponseDTO.userErrorParam(String.format("与前%d个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseDTO.ok();
|
return ResponseDTO.ok();
|
||||||
@ -143,7 +147,14 @@ public class SecurityPasswordService {
|
|||||||
* 获取 加密后 的密码
|
* 获取 加密后 的密码
|
||||||
*/
|
*/
|
||||||
public static String getEncryptPwd(String password) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<springboot.version>2.7.18</springboot.version>
|
<springboot.version>2.7.18</springboot.version>
|
||||||
<spring-mock.version>2.0.8</spring-mock.version>
|
<spring-mock.version>2.0.8</spring-mock.version>
|
||||||
|
<spring-security-crypto.version>5.8.16</spring-security-crypto.version>
|
||||||
<mybatis-plus.version>3.5.2</mybatis-plus.version>
|
<mybatis-plus.version>3.5.2</mybatis-plus.version>
|
||||||
<mysql-connector-j.version>8.0.33</mysql-connector-j.version>
|
<mysql-connector-j.version>8.0.33</mysql-connector-j.version>
|
||||||
<p6spy.version>3.9.1</p6spy.version>
|
<p6spy.version>3.9.1</p6spy.version>
|
||||||
@ -50,7 +51,7 @@
|
|||||||
<velocity-tools.version>3.1</velocity-tools.version>
|
<velocity-tools.version>3.1</velocity-tools.version>
|
||||||
<sa-token.version>1.37.0</sa-token.version>
|
<sa-token.version>1.37.0</sa-token.version>
|
||||||
<ip2region.version>2.7.0</ip2region.version>
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
<bcprov.version>1.59</bcprov.version>
|
<bcprov.version>1.80</bcprov.version>
|
||||||
<jackson-datatype-jsr310.version>2.13.4</jackson-datatype-jsr310.version>
|
<jackson-datatype-jsr310.version>2.13.4</jackson-datatype-jsr310.version>
|
||||||
<jackson-dataformat-yaml.version>2.16.1</jackson-dataformat-yaml.version>
|
<jackson-dataformat-yaml.version>2.16.1</jackson-dataformat-yaml.version>
|
||||||
<smartdb.version>1.2.0</smartdb.version>
|
<smartdb.version>1.2.0</smartdb.version>
|
||||||
@ -58,6 +59,7 @@
|
|||||||
<snakeyaml.version>2.2</snakeyaml.version>
|
<snakeyaml.version>2.2</snakeyaml.version>
|
||||||
<freemarker.version>2.3.33</freemarker.version>
|
<freemarker.version>2.3.33</freemarker.version>
|
||||||
<jsoup.version>1.18.1</jsoup.version>
|
<jsoup.version>1.18.1</jsoup.version>
|
||||||
|
<tika.version>2.9.3</tika.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -85,6 +87,12 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-crypto</artifactId>
|
||||||
|
<version>${spring-security-crypto.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mysql</groupId>
|
<groupId>com.mysql</groupId>
|
||||||
<artifactId>mysql-connector-j</artifactId>
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
@ -261,7 +269,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk15on</artifactId>
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
<version>${bcprov.version}</version>
|
<version>${bcprov.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
@ -363,6 +371,12 @@
|
|||||||
<version>${freemarker.version}</version>
|
<version>${freemarker.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>${tika.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
@ -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.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -34,6 +36,7 @@ public class DepartmentEntity {
|
|||||||
/**
|
/**
|
||||||
* 负责人员工 id
|
* 负责人员工 id
|
||||||
*/
|
*/
|
||||||
|
@TableField(updateStrategy = FieldStrategy.IGNORED)
|
||||||
private Long managerId;
|
private Long managerId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -297,38 +297,39 @@ public class EmployeeService {
|
|||||||
if (employeeEntity == null) {
|
if (employeeEntity == null) {
|
||||||
return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST);
|
return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验原始密码
|
// 校验原始密码
|
||||||
String oldPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getOldPassword());
|
if (!SecurityPasswordService.matchesPwd(updatePasswordForm.getOldPassword(),employeeEntity.getLoginPwd()) ) {
|
||||||
if (!Objects.equals(oldPassword, employeeEntity.getLoginPwd())) {
|
|
||||||
return ResponseDTO.userErrorParam("原密码有误,请重新输入");
|
return ResponseDTO.userErrorParam("原密码有误,请重新输入");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新旧密码相同
|
||||||
|
if (Objects.equals(updatePasswordForm.getOldPassword(), updatePasswordForm.getNewPassword()) ){
|
||||||
|
return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入");
|
||||||
|
}
|
||||||
|
|
||||||
// 校验密码复杂度
|
// 校验密码复杂度
|
||||||
ResponseDTO<String> validatePassComplexity = securityPasswordService.validatePasswordComplexity(updatePasswordForm.getNewPassword());
|
ResponseDTO<String> validatePassComplexity = securityPasswordService.validatePasswordComplexity(updatePasswordForm.getNewPassword());
|
||||||
if (!validatePassComplexity.getOk()) {
|
if (!validatePassComplexity.getOk()) {
|
||||||
return validatePassComplexity;
|
return validatePassComplexity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新旧密码相同
|
|
||||||
String newPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword());
|
|
||||||
if (Objects.equals(oldPassword, newPassword)) {
|
|
||||||
return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据三级等保规则,校验密码是否重复
|
// 根据三级等保规则,校验密码是否重复
|
||||||
ResponseDTO<String> passwordRepeatTimes = securityPasswordService.validatePasswordRepeatTimes(requestUser, updatePasswordForm.getNewPassword());
|
ResponseDTO<String> passwordRepeatTimes = securityPasswordService.validatePasswordRepeatTimes(requestUser, updatePasswordForm.getNewPassword());
|
||||||
if (!passwordRepeatTimes.getOk()) {
|
if (!passwordRepeatTimes.getOk()) {
|
||||||
return ResponseDTO.error(passwordRepeatTimes);
|
return ResponseDTO.error(passwordRepeatTimes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 更新密码
|
// 更新密码
|
||||||
|
String newEncryptPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword());
|
||||||
EmployeeEntity updateEntity = new EmployeeEntity();
|
EmployeeEntity updateEntity = new EmployeeEntity();
|
||||||
updateEntity.setEmployeeId(employeeId);
|
updateEntity.setEmployeeId(employeeId);
|
||||||
updateEntity.setLoginPwd(newPassword);
|
updateEntity.setLoginPwd(newEncryptPassword);
|
||||||
employeeDao.updateById(updateEntity);
|
employeeDao.updateById(updateEntity);
|
||||||
|
|
||||||
// 保存修改密码密码记录
|
// 保存修改密码密码记录
|
||||||
securityPasswordService.saveUserChangePasswordLog(requestUser, newPassword, oldPassword);
|
securityPasswordService.saveUserChangePasswordLog(requestUser, newEncryptPassword, employeeEntity.getLoginPwd());
|
||||||
|
|
||||||
return ResponseDTO.ok();
|
return ResponseDTO.ok();
|
||||||
}
|
}
|
||||||
|
@ -164,10 +164,15 @@ public class LoginService implements StpInterface {
|
|||||||
// 验证登录名
|
// 验证登录名
|
||||||
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginForm.getLoginName());
|
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginForm.getLoginName());
|
||||||
if (null == employeeEntity) {
|
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()) {
|
if (employeeEntity.getDisabledFlag()) {
|
||||||
saveLoginLog(employeeEntity, ip, userAgent, "账号已禁用", LoginLogResultEnum.LOGIN_FAIL);
|
saveLoginLog(employeeEntity, ip, userAgent, "账号已禁用", LoginLogResultEnum.LOGIN_FAIL);
|
||||||
return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!");
|
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);
|
saveLoginLog(employeeEntity, ip, userAgent, "密码错误", LoginLogResultEnum.LOGIN_FAIL);
|
||||||
// 记录等级保护次数
|
// 记录等级保护次数
|
||||||
@ -506,10 +511,14 @@ public class LoginService implements StpInterface {
|
|||||||
// 验证登录名
|
// 验证登录名
|
||||||
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginName);
|
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginName);
|
||||||
if (null == employeeEntity) {
|
if (null == employeeEntity) {
|
||||||
return ResponseDTO.userErrorParam("登录名不存在!");
|
return ResponseDTO.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证账号状态
|
// 验证账号状态
|
||||||
|
if (employeeEntity.getDeletedFlag()) {
|
||||||
|
return ResponseDTO.userErrorParam("您的账号已被删除,请联系工作人员!");
|
||||||
|
}
|
||||||
|
|
||||||
if (employeeEntity.getDisabledFlag()) {
|
if (employeeEntity.getDisabledFlag()) {
|
||||||
return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!");
|
return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!");
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,11 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-crypto</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mysql</groupId>
|
<groupId>com.mysql</groupId>
|
||||||
<artifactId>mysql-connector-j</artifactId>
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
@ -225,7 +230,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk15on</artifactId>
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -298,6 +303,11 @@
|
|||||||
<artifactId>freemarker</artifactId>
|
<artifactId>freemarker</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>${tika.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
@ -162,6 +162,10 @@ public class Level3ProtectConfigService {
|
|||||||
this.maxUploadFileSizeMb = configForm.getMaxUploadFileSizeMb();
|
this.maxUploadFileSizeMb = configForm.getMaxUploadFileSizeMb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configForm.getLoginFailMaxTimes() != null) {
|
||||||
|
this.loginFailMaxTimes = configForm.getLoginFailMaxTimes();
|
||||||
|
}
|
||||||
|
|
||||||
if (configForm.getLoginFailLockMinutes() != null) {
|
if (configForm.getLoginFailLockMinutes() != null) {
|
||||||
this.loginFailLockSeconds = configForm.getLoginFailLockMinutes() * 60;
|
this.loginFailLockSeconds = configForm.getLoginFailLockMinutes() * 60;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,23 @@
|
|||||||
package net.lab1024.sa.base.module.support.securityprotect.service;
|
package net.lab1024.sa.base.module.support.securityprotect.service;
|
||||||
|
|
||||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
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.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 三级等保 文件上传 相关
|
* 三级等保 文件上传 相关
|
||||||
@ -23,6 +35,28 @@ public class SecurityFileService {
|
|||||||
@Resource
|
@Resource
|
||||||
private Level3ProtectConfigService level3ProtectConfigService;
|
private Level3ProtectConfigService level3ProtectConfigService;
|
||||||
|
|
||||||
|
// 定义白名单MIME类型
|
||||||
|
private static final List<String> 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()) {
|
if (level3ProtectConfigService.isFileDetectFlag()) {
|
||||||
return ResponseDTO.ok();
|
String fileType = getFileMimeType(file);
|
||||||
|
if(ALLOWED_MIME_TYPES.stream()
|
||||||
|
.noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){
|
||||||
|
return ResponseDTO.userErrorParam("禁止上传此文件类型");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测文件类型
|
|
||||||
// .....
|
|
||||||
|
|
||||||
return ResponseDTO.ok();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;
|
|
@ -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.common.util.SmartStringUtil;
|
||||||
import net.lab1024.sa.base.module.support.securityprotect.dao.PasswordLogDao;
|
import net.lab1024.sa.base.module.support.securityprotect.dao.PasswordLogDao;
|
||||||
import net.lab1024.sa.base.module.support.securityprotect.domain.PasswordLogEntity;
|
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.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@ -46,6 +46,8 @@ public class SecurityPasswordService {
|
|||||||
@Resource
|
@Resource
|
||||||
private Level3ProtectConfigService level3ProtectConfigService;
|
private Level3ProtectConfigService level3ProtectConfigService;
|
||||||
|
|
||||||
|
static Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验密码复杂度
|
* 校验密码复杂度
|
||||||
*/
|
*/
|
||||||
@ -84,8 +86,9 @@ public class SecurityPasswordService {
|
|||||||
|
|
||||||
// 检查最近几次是否有重复密码
|
// 检查最近几次是否有重复密码
|
||||||
List<String> oldPasswords = passwordLogDao.selectOldPassword(requestUser.getUserType().getValue(), requestUser.getUserId(), level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes());
|
List<String> oldPasswords = passwordLogDao.selectOldPassword(requestUser.getUserType().getValue(), requestUser.getUserId(), level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes());
|
||||||
if (oldPasswords != null && oldPasswords.contains(getEncryptPwd(newPassword))) {
|
boolean isDuplicate = oldPasswords.stream().anyMatch(oldPassword -> encoder.matches(newPassword, oldPassword));
|
||||||
return ResponseDTO.userErrorParam(String.format("与前%s个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes()));
|
if (isDuplicate) {
|
||||||
|
return ResponseDTO.userErrorParam(String.format("与前%d个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseDTO.ok();
|
return ResponseDTO.ok();
|
||||||
@ -143,7 +146,14 @@ public class SecurityPasswordService {
|
|||||||
* 获取 加密后 的密码
|
* 获取 加密后 的密码
|
||||||
*/
|
*/
|
||||||
public static String getEncryptPwd(String password) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,16 +11,18 @@
|
|||||||
<a-modal v-model:open="visible" :title="formState.departmentId ? '编辑部门' : '添加部门'" @ok="handleOk" destroyOnClose>
|
<a-modal v-model:open="visible" :title="formState.departmentId ? '编辑部门' : '添加部门'" @ok="handleOk" destroyOnClose>
|
||||||
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical">
|
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical">
|
||||||
<a-form-item label="上级部门" name="parentId" v-if="formState.parentId != 0">
|
<a-form-item label="上级部门" name="parentId" v-if="formState.parentId != 0">
|
||||||
<DepartmentTreeSelect ref="departmentTreeSelect" v-model:value="formState.parentId" :defaultValueFlag="false" width="100%" />
|
<DepartmentTreeSelect ref="departmentTreeSelect" v-model:value="formState.parentId" :defaultValueFlag="false"
|
||||||
|
width="100%" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="部门名称" name="name">
|
<a-form-item label="部门名称" name="name">
|
||||||
<a-input v-model:value.trim="formState.name" placeholder="请输入部门名称" />
|
<a-input v-model:value.trim="formState.name" placeholder="请输入部门名称" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="部门负责人" name="managerId">
|
<a-form-item label="部门负责人" name="managerId">
|
||||||
<EmployeeSelect ref="employeeSelect" placeholder="请选择部门负责人" width="100%" v-model:value="formState.managerId" :leaveFlag="false" />
|
<EmployeeSelect ref="employeeSelect" placeholder="请选择部门负责人" width="100%" v-model:value="formState.managerId"
|
||||||
|
:leaveFlag="false" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="部门排序 (值越大越靠前!)" name="sort">
|
<a-form-item label="部门排序 (值越大越靠前!)" name="sort">
|
||||||
<a-input-number style="width: 100%" v-model:value="formState.sort" :min="0" placeholder="请输入部门名称" />
|
<a-input-number style="width: 100%" v-model:value="formState.sort" :min="0" placeholder="请输入部门排序" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@ -75,7 +77,6 @@
|
|||||||
{ required: true, message: '部门名称不能为空' },
|
{ required: true, message: '部门名称不能为空' },
|
||||||
{ max: 50, message: '部门名称不能大于20个字符', trigger: 'blur' },
|
{ max: 50, message: '部门名称不能大于20个字符', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
managerId: [{ required: true, message: '部门负责人不能为空' }],
|
|
||||||
};
|
};
|
||||||
// 更新表单数据
|
// 更新表单数据
|
||||||
function updateFormData(data) {
|
function updateFormData(data) {
|
||||||
@ -123,7 +124,7 @@
|
|||||||
SmartLoading.show();
|
SmartLoading.show();
|
||||||
try {
|
try {
|
||||||
if (formState.parentId == formState.departmentId) {
|
if (formState.parentId == formState.departmentId) {
|
||||||
message.warning('上级菜单不能为自己');
|
message.warning('上级部门不能为自己');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await departmentApi.updateDepartment(formState);
|
await departmentApi.updateDepartment(formState);
|
||||||
|
@ -11,16 +11,18 @@
|
|||||||
<a-modal v-model:open="visible" :title="formState.departmentId ? '编辑部门' : '添加部门'" @ok="handleOk" destroyOnClose>
|
<a-modal v-model:open="visible" :title="formState.departmentId ? '编辑部门' : '添加部门'" @ok="handleOk" destroyOnClose>
|
||||||
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical">
|
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical">
|
||||||
<a-form-item label="上级部门" name="parentId" v-if="formState.parentId != 0">
|
<a-form-item label="上级部门" name="parentId" v-if="formState.parentId != 0">
|
||||||
<DepartmentTreeSelect ref="departmentTreeSelect" v-model:value="formState.parentId" :defaultValueFlag="false" width="100%" />
|
<DepartmentTreeSelect ref="departmentTreeSelect" v-model:value="formState.parentId" :defaultValueFlag="false"
|
||||||
|
width="100%" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="部门名称" name="name">
|
<a-form-item label="部门名称" name="name">
|
||||||
<a-input v-model:value.trim="formState.name" placeholder="请输入部门名称" />
|
<a-input v-model:value.trim="formState.name" placeholder="请输入部门名称" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="部门负责人" name="managerId">
|
<a-form-item label="部门负责人" name="managerId">
|
||||||
<EmployeeSelect ref="employeeSelect" placeholder="请选择部门负责人" width="100%" v-model:value="formState.managerId" :leaveFlag="false" />
|
<EmployeeSelect ref="employeeSelect" placeholder="请选择部门负责人" width="100%" v-model:value="formState.managerId"
|
||||||
|
:leaveFlag="false" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="部门排序 (值越大越靠前!)" name="sort">
|
<a-form-item label="部门排序 (值越大越靠前!)" name="sort">
|
||||||
<a-input-number style="width: 100%" v-model:value="formState.sort" :min="0" placeholder="请输入部门名称" />
|
<a-input-number style="width: 100%" v-model:value="formState.sort" :min="0" placeholder="请输入部门排序" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@ -75,7 +77,6 @@
|
|||||||
{ required: true, message: '部门名称不能为空' },
|
{ required: true, message: '部门名称不能为空' },
|
||||||
{ max: 50, message: '部门名称不能大于20个字符', trigger: 'blur' },
|
{ max: 50, message: '部门名称不能大于20个字符', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
managerId: [{ required: true, message: '部门负责人不能为空' }],
|
|
||||||
};
|
};
|
||||||
// 更新表单数据
|
// 更新表单数据
|
||||||
function updateFormData(data) {
|
function updateFormData(data) {
|
||||||
@ -123,7 +124,7 @@
|
|||||||
SmartLoading.show();
|
SmartLoading.show();
|
||||||
try {
|
try {
|
||||||
if (formState.parentId == formState.departmentId) {
|
if (formState.parentId == formState.departmentId) {
|
||||||
message.warning('上级菜单不能为自己');
|
message.warning('上级部门不能为自己');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await departmentApi.updateDepartment(formState);
|
await departmentApi.updateDepartment(formState);
|
||||||
|
@ -238,7 +238,7 @@ DROP TABLE IF EXISTS `t_employee`;
|
|||||||
CREATE TABLE `t_employee` (
|
CREATE TABLE `t_employee` (
|
||||||
`employee_id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键',
|
`employee_id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
`login_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL 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 '员工名称',
|
`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,
|
`avatar` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
`gender` tinyint(1) NOT NULL DEFAULT 0 COMMENT '性别',
|
`gender` tinyint(1) NOT NULL DEFAULT 0 COMMENT '性别',
|
||||||
@ -258,20 +258,20 @@ CREATE TABLE `t_employee` (
|
|||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of 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 (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', '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 (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', '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 (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', '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 (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', '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 (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', '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 (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', '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 (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', '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 (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', '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 (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', '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 (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', '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 (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', '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 (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', '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 (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', '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 (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
|
-- Table structure for t_feedback
|
||||||
|
Loading…
Reference in New Issue
Block a user