This commit is contained in:
2025-04-20 08:02:06 +08:00
parent 53fa8a059c
commit 2c79d3c37e
32 changed files with 753 additions and 72 deletions

View File

@@ -15,6 +15,7 @@
common通用工具
</description>
<dependencies>
<!-- Spring框架基本的核心工具 -->
@@ -156,6 +157,13 @@
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,130 @@
package com.ruoyi.common.utils;
import java.util.regex.Pattern;
public class PasswordValidator {
// 禁止使用的常见弱口令列表
private static final String[] WEAK_PASSWORDS = {"123456@cxxm","123456", "password", "admin", "abc123", "qwerty"};
// 正则表达式模式
private static final Pattern UPPERCASE_PATTERN = Pattern.compile("[A-Z]");
private static final Pattern LOWERCASE_PATTERN = Pattern.compile("[a-z]");
private static final Pattern DIGIT_PATTERN = Pattern.compile("[0-9]");
private static final Pattern SPECIAL_CHAR_PATTERN = Pattern.compile("[!@#$%^&*()\\-_+=\\[\\]{}|;:,.<>/?]");
public static boolean isValidPasswordApi(String password){
String userInfo =SecurityUtils.getUsername();
// 检查是否为空或太短
if (StringUtils.isEmpty(password) || password.length() < 8) {
throw new RuntimeException("密码长度不能小于8位");
}
// 检查是否包含用户信息
if (StringUtils.isNotEmpty(userInfo) && password.toLowerCase().contains(userInfo.toLowerCase())) {
throw new RuntimeException("密码不得包含用户信息");
}
// 检查是否为常见弱口令
for (String weakPassword : WEAK_PASSWORDS) {
if (password.equals(weakPassword)) {
throw new RuntimeException("密码不得包含常见弱口令");
}
}
// 检查是否包含重复字符或简单模式
if (isSimplePattern(password)) {
throw new RuntimeException("密码不得包含连续重复字符或简单模式");
}
// 检查是否包含至少三种不同类型字符
int charTypesCount = 0;
if (UPPERCASE_PATTERN.matcher(password).find()) {
charTypesCount++;
}
if (LOWERCASE_PATTERN.matcher(password).find()) {
charTypesCount++;
}
if (DIGIT_PATTERN.matcher(password).find()) {
charTypesCount++;
}
if (SPECIAL_CHAR_PATTERN.matcher(password).find()) {
charTypesCount++;
}
if (charTypesCount < 3){
throw new RuntimeException("密码必须包含至少三种不同类型字符");
}
return charTypesCount >= 3;
}
/**
* 验证密码强度
*
* @param password 待验证的密码
* @param userInfo 用户信息,用于检查是否包含用户账号、姓名、工号、手机号、邮箱等
* @return 如果密码强度足够则返回true否则返回false
*/
public static boolean isValidPassword(String password, String userInfo){
// 检查是否为空或太短
if (StringUtils.isEmpty(password) || password.length() < 8) {
return false;
}
// 检查是否包含用户信息
if (StringUtils.isNotEmpty(userInfo) && password.toLowerCase().contains(userInfo.toLowerCase())) {
return false;
}
// 检查是否为常见弱口令
for (String weakPassword : WEAK_PASSWORDS) {
if (password.equals(weakPassword)) {
return false;
}
}
// 检查是否包含重复字符或简单模式
if (isSimplePattern(password)) {
return false;
}
// 检查是否包含至少三种不同类型字符
int charTypesCount = 0;
if (UPPERCASE_PATTERN.matcher(password).find()) {
charTypesCount++;
}
if (LOWERCASE_PATTERN.matcher(password).find()) {
charTypesCount++;
}
if (DIGIT_PATTERN.matcher(password).find()) {
charTypesCount++;
}
if (SPECIAL_CHAR_PATTERN.matcher(password).find()) {
charTypesCount++;
}
return charTypesCount >= 3;
}
/**
* 检查密码是否包含重复字符或简单模式
*
* @param password 待检查的密码
* @return 如果包含重复字符或简单模式则返回true否则返回false
*/
private static boolean isSimplePattern(String password) {
// 检查简单模式(这里仅作为示例,您可以根据需要扩展)
if (password.matches("(.)\\1{2}") || password.matches("\\d+") || password.matches("[a-zA-Z]+")) {
return true;
}
return false;
}
public static void main(String[] args) {
// 测试密码校验
String[] testPasswords = {"123456@cxxm", "abbbcd@@@", "StrongPasss1!", "A1b!2cD3", "123456", "password", "admin", "abc123", "qwerty"};
String userInfo = "admin"; // 示例用户信息
for (String password : testPasswords) {
System.out.println("Password: " + password + " is valid: " + isValidPassword(password, userInfo));
}
}
}

View File

@@ -6,8 +6,15 @@ import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@Slf4j
public class ResourceUtils {
@@ -30,4 +37,77 @@ public class ResourceUtils {
log.error("下载文件失败", e);
}
}
/**
* 多文件下载
*
* @param files
* @param zipFile
* @param response
*/
public static void multiDownload(List<File> files,
File zipFile,
HttpServletResponse response) {
response.reset();
// 设置response的Header
response.setCharacterEncoding("UTF-8");
//Content-Disposition的作用告知浏览器以何种方式显示响应返回的文件用浏览器打开还是以附件的形式下载到本地保存
//attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
// filename表示文件的默认名称因为网络传输只支持URL编码的相关支付因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
try {
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFile.getName(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 告知浏览器文件的大小
//设置响应格式,已文件流的方式返回给前端。
response.setContentType("application/octet-stream");
//生成压缩文件
ZipMultiFile(files, zipFile);
response.addHeader("Content-Length", String.valueOf(zipFile.length()));
try (ServletOutputStream sos = response.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(zipFile.toPath()))) {
byte[] buff = new byte[1024 * 10];
int index;
while ((index = bis.read(buff, 0, buff.length)) != -1) {
sos.write(buff, 0, index);
}
sos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param files 需要压缩的文件列表
* @param zipFile 压缩后的文件
*/
public static void ZipMultiFile(List<File> files, File zipFile) {
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()))) {
for (File file : files) {
try (FileInputStream fis = new FileInputStream(file);) {
zipOut.putNextEntry(new ZipEntry(file.getName()));
int temp;
byte[] buf = new byte[1024];
while ((temp = fis.read(buf)) != -1) {
zipOut.write(buf, 0, temp);
}
zipOut.closeEntry();
zipOut.flush();
} catch (Exception ex) {
ex.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}