后端代码提交

This commit is contained in:
cwchen 2025-09-04 10:09:10 +08:00
parent eccfdc99e8
commit 6f67f1b6e9
18 changed files with 1283 additions and 4 deletions

View File

@ -16,7 +16,7 @@ public class BonusApplication
{
// System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(BonusApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 智慧图档启动成功 ლ(´ڡ`ლ)゙ \n" +
System.out.println("(♥◠‿◠)ノ゙ 工程档案资料移交启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +

View File

@ -113,7 +113,7 @@ public class SwaggerConfig
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题:若依管理系统_接口文档")
.title("标题:管理系统_接口文档")
// 描述
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
// 作者信息

View File

@ -119,6 +119,28 @@
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-rsa</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,16 @@
package com.bonus.common.exception;
/**
* 验证码错误异常类
*
* @author bonus
*/
public class CaptchaException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public CaptchaException(String msg)
{
super(msg);
}
}

View File

@ -0,0 +1,332 @@
package com.bonus.common.filter;
import com.bonus.common.exception.CaptchaException;
import com.bonus.common.utils.encryption.Sm3Util;
import com.bonus.common.utils.encryption.Sm4Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StreamUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* 请求内容存储 处理请求内容 内容放在gatewayContext中
* 解决数据流被重复读取无数据的 问题
* 对formData 数据进行解密
*
* @author bonus
*/
@Component
@Slf4j
public class RequestCoverFilter implements Filter {
/**
* 数据加密标志
*/
public static final String ENCRYPT = "encryptRequest";
/**
* 数据完整性校验标志
*/
public static final String INTEGRALITY = "checkIntegrity";
/**
* 完整性校验哈希值
*/
public static final String HMAC_HEADER_NAME = "Params-Hash";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化逻辑
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 获取请求头信息
boolean integrality = "true".equalsIgnoreCase(httpRequest.getHeader(INTEGRALITY));
boolean encrypt = "true".equalsIgnoreCase(httpRequest.getHeader(ENCRYPT));
String contentType = httpRequest.getContentType();
// 处理不同类型的请求
if (contentType == null) {
log.info("请求头中无Content-Type信息处理URL参数。");
handleUrlParams(httpRequest, response, chain, integrality, encrypt);
} else if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
handleBodyRequest(httpRequest, response, chain, integrality, encrypt);
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
// 清理逻辑
}
/**
* 处理请求体里的参数
*/
private void handleBodyRequest(HttpServletRequest request, ServletResponse response,
FilterChain chain, boolean integrality, boolean encrypt)
throws IOException, ServletException {
// 读取请求体内容
String requestBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
if (ObjectUtils.isEmpty(requestBody)) {
chain.doFilter(request, response);
return;
}
// 去掉多余的引号如果有
if (requestBody.startsWith("\"") && requestBody.endsWith("\"")) {
requestBody = requestBody.substring(1, requestBody.length() - 1);
}
try {
// 解密请求体
if (encrypt) {
requestBody = Sm4Utils.decrypt(requestBody);
}
if (ObjectUtils.isEmpty(requestBody)) {
chain.doFilter(request, response);
return;
}
// 校验数据完整性
if (integrality) {
String[] parts = requestBody.split("\\|");
if (parts.length != 2) {
log.error("解密后的请求体格式不正确: {}", requestBody);
throw new CaptchaException("请求参数不正确");
}
integrityVerification(parts[1], parts[0]);
requestBody = parts[0];
}
if (ObjectUtils.isEmpty(requestBody)) {
chain.doFilter(request, response);
return;
}
// 创建包装的请求对象
HttpServletRequest wrappedRequest = new BodyCachingRequestWrapper(request, requestBody);
chain.doFilter(wrappedRequest, response);
} catch (Exception e) {
log.error("处理请求体时发生错误: {}", e.getMessage(), e);
throw new ServletException("请求处理失败", e);
}
}
/**
* 处理URL参数
*/
private void handleUrlParams(HttpServletRequest request, ServletResponse response,
FilterChain chain, boolean integrality, boolean encrypt)
throws IOException, ServletException {
try {
HttpServletRequest updatedRequest = updateRequestParam(request, integrality, encrypt);
chain.doFilter(updatedRequest != null ? updatedRequest : request, response);
} catch (Exception e) {
log.error("处理 GET 请求时发生错误: {}", e.getMessage(), e);
throw new ServletException("请求处理失败", e);
}
}
/**
* 更新查询参数解密和验证数据完整性
*/
private HttpServletRequest updateRequestParam(HttpServletRequest request,
boolean integrality, boolean encrypt) {
String queryString = request.getQueryString();
if (!ObjectUtils.isEmpty(queryString)) {
try {
// 解析 params 参数
String encryptedParams = null;
String[] queryParams = queryString.split("&");
for (String param : queryParams) {
if (param.startsWith("params=")) {
encryptedParams = param.substring(7); // 去掉 "params="
break;
}
}
if (encryptedParams == null) {
log.warn("未找到加密参数,跳过解密处理");
return null;
}
// URL解码
encryptedParams = java.net.URLDecoder.decode(encryptedParams, StandardCharsets.UTF_8.name());
String query = encryptedParams;
// 解密查询参数
if (encrypt) {
query = Sm4Utils.decrypt(query);
}
// 校验数据完整性
if (integrality) {
String[] parts = query.split("\\|");
if (parts.length != 2) {
log.error("解密后的参数格式不正确: {}", query);
throw new CaptchaException("请求参数不正确");
}
integrityVerification(parts[1], parts[0]);
query = parts[0]; // 只保留原始参数部分
}
if (ObjectUtils.isEmpty(query)) {
return null;
}
// 创建新的请求对象
return new QueryStringRequestWrapper(request, query);
} catch (Exception e) {
log.error("解密查询参数时发生错误: {}", e.getMessage(), e);
throw new CaptchaException("请求参数不正确");
}
}
return null;
}
/**
* 数据完整性校验
*/
private void integrityVerification(String providedHmac, String query) {
if (providedHmac == null) {
log.error("完整性校验哈希值为空");
throw new CaptchaException("请求参数不正确");
}
String calculatedHash = Sm3Util.encrypt(query);
log.debug("计算出的哈希值: {}", calculatedHash);
log.debug("提供的哈希值: {}", providedHmac);
if (!calculatedHash.equals(providedHmac)) {
log.error("参数完整性校验失败");
throw new CaptchaException("请求参数不正确");
}
}
/**
* 请求体包装类
*/
private static class BodyCachingRequestWrapper extends HttpServletRequestWrapper {
private final String body;
public BodyCachingRequestWrapper(HttpServletRequest request, String body) {
super(request);
this.body = body;
}
@Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
return new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
// 不需要实现
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
/**
* 查询字符串包装类
*/
private static class QueryStringRequestWrapper extends HttpServletRequestWrapper {
private final String queryString;
public QueryStringRequestWrapper(HttpServletRequest request, String queryString) {
super(request);
this.queryString = queryString;
}
@Override
public String getQueryString() {
return queryString;
}
@Override
public String getParameter(String name) {
// 解析queryString获取参数值
if (queryString != null) {
String[] pairs = queryString.split("&");
for (String pair : pairs) {
String[] keyValue = pair.split("=");
if (keyValue.length == 2 && keyValue[0].equals(name)) {
return keyValue[1];
}
}
}
return super.getParameter(name);
}
@Override
public Map<String, String[]> getParameterMap() {
// 解析queryString为参数Map
Map<String, String[]> parameterMap = new HashMap<>();
if (queryString != null) {
String[] pairs = queryString.split("&");
for (String pair : pairs) {
String[] keyValue = pair.split("=");
if (keyValue.length == 2) {
parameterMap.put(keyValue[0], new String[]{keyValue[1]});
}
}
}
return parameterMap;
}
@Override
public Enumeration<String> getParameterNames() {
Map<String, String[]> parameterMap = getParameterMap();
return Collections.enumeration(parameterMap.keySet());
}
@Override
public String[] getParameterValues(String name) {
return getParameterMap().get(name);
}
}
}

View File

@ -0,0 +1,101 @@
package com.bonus.common.filter;
import com.bonus.common.utils.encryption.Sm4Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 对返回的 data数据进行加密
*
* @author 黑子
*/
@Configuration
@Slf4j
public class ResponseEncryptFilter implements Filter {
/**
* 加密标识
*/
private static final String ENCRYPT_RESPONSE = "encryptResponse";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化逻辑
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 检查请求头中是否包含加密标志
if (shouldEncrypt(httpRequest)) {
// 使用响应包装器来缓存响应内容
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(httpResponse);
try {
chain.doFilter(request, responseWrapper);
// 获取响应状态码
int status = responseWrapper.getStatus();
// 只处理状态码为200 OK的响应
if (status == HttpServletResponse.SC_OK) {
// 获取响应内容
byte[] responseBody = responseWrapper.getContentAsByteArray();
if (responseBody != null && responseBody.length > 0) {
String responseData = new String(responseBody, StandardCharsets.UTF_8);
// 加密响应数据
String encryptedData = Sm4Utils.encrypt(responseData);
byte[] encryptedContent = encryptedData.getBytes(StandardCharsets.UTF_8);
// 设置加密后的响应
responseWrapper.resetBuffer();
responseWrapper.setContentLength(encryptedContent.length);
responseWrapper.getOutputStream().write(encryptedContent);
// 添加响应头标识
responseWrapper.setHeader(ENCRYPT_RESPONSE, "true");
}
}
} catch (Exception e) {
log.error("响应加密处理时发生错误: {}", e.getMessage(), e);
throw new ServletException("响应处理失败", e);
} finally {
// 确保响应被提交
responseWrapper.copyBodyToResponse();
}
} else {
// 不需要加密直接继续处理链
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
// 清理逻辑
}
/**
* 判断是否需要对响应数据进行加密
*
* @param request 请求对象
* @return 如果需要加密返回true否则返回false
*/
private boolean shouldEncrypt(HttpServletRequest request) {
String encryptHeader = request.getHeader(ENCRYPT_RESPONSE);
return "true".equalsIgnoreCase(encryptHeader);
}
}

View File

@ -0,0 +1,120 @@
package com.bonus.common.utils.encryption;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
/**
* AES加密工具类
*
* @author HeiZi
*/
@Slf4j
public class AesCbcUtils {
//使用AES-256-CBC加密模式key需要为16位,key和iv可以相同
/**
* 密钥算法
*/
private static final String KEY_ALGORITHM = "AES";
/**
* 加密/解密算法 / 工作模式 / 填充方式
* Java 6支持PKCS5Padding填充方式
* Bouncy Castle支持PKCS7Padding填充方式
*/
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
/**
* 偏移量只有CBC模式才需要
*/
private final static String IV_PARAMETER = "1234567812345678";
/**
* AES要求密钥长度为128位或192位或256位java默认限制AES密钥长度最多128位
*/
public static String sKey = "zhgd@bonus@zhgd@bonus@1234567890";
/**
* 编码格式导出
*/
public static final String ENCODING = "utf-8";
static {
//如果是PKCS7Padding填充方式则必须加上下面这行
Security.addProvider(new BouncyCastleProvider());
}
/**
* AES加密
* CBC模式
*
* @param source 源字符串
* @param
* @return 加密后的密文
* @throws Exception
*/
public static String encrypt(String source, String key, String encoding, String ivParameter, String cipherAlgorithm, String keyAlgorithm) {
try {
byte[] sourceBytes = source.getBytes(encoding);
byte[] keyBytes = key.getBytes(encoding);
Cipher cipher = Cipher.getInstance(cipherAlgorithm, "BC");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes(encoding));
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, keyAlgorithm), iv);
byte[] decrypted = cipher.doFinal(sourceBytes);
return Base64.encodeBase64String(decrypted);
} catch (Exception e) {
log.error(e.toString(), e);
}
return null;
}
// public static void main(String[] args) {
// // String json="";
//
// String json="{\"username\":\"guest\",\"password\":\"admin@123\"}";
// String data=encrypt(json);
// System.err.println(data);
// String jm=decrypt("\"yeCxaGhmdDt+zDsKPr1prukR15BfIMLMOYIFKdgffGpMV76qVD8ANU4zsLiRZ87+7hd1Yje182nHhUOp/TeYCRuuUPQzmuXH7ikYVEWR1Rmc8uLq3G/BTMU6wFMSRFDBXARRl+dvnpyfX+MleF0KB1OAeuOIKv1gQPskmvDiFAniUlowf/96ZzMl7Bokqc/Lse8lMV92IQB14yQNa0+u/2kvdirzrcq+HCt9K8Ot3C59mjDqg49WoM65LEmaHmZqzdmmjbQlGH7ZOAFTLvF5kPzUsMz5Uim1uNvByB3eLFohc1UgB28DWoTyh43sRMPpq3S1BB20gcrBf3uVra/4qehBr3z98CGhGRyKHbjsCKl0Ri6YO1rp5aMRd9Y3wbOJFrZXFag5iifQx+ooDBeu6h1KCb5JfCjyAmw9+pgEL6X3eELPzK13XjblDyEikjx2Edv45MGsC0DUcjvz/Sb4E/8rn3o1Ep7W31xNCdn5mzZ8VO9POhE3DMK6woeN2C7TW+7YO/Zs9e4zKLS4vThvk5urCn2Ff2HkVBzoPtP2imuqQrY8898sbLllyaJEG0DPSrCf985ZgVa03JsO/EkMr3KAiHV5SHBZS1XEXqjdpL+YEdOnlfBXUk83kJhLj9rhUrTFza7ednQSzjq4XpIJJVy2aJhhj1chsmIM1Xl//0Dbak9Lb6VUq5Xr2IFAjNTgyxwtcFCdEvp4YZCSP6kqWgEpeAVlejyYOcNckUnWkjeHjtfgwvVsvUjWzyIRFCa7m/oY28xBV16RmW/r4XXquqKVdbPtATzrmf7pJCynXZ3IPd9ZGc1OTLnss9Ln9XNTH0E/I4Ma95fn9uxA+sOQkQ==\"");
// String jiemi=decrypt(data);
// System.err.println(jm);
// System.err.println(jiemi);
// }
/**
* AES解密
* CBC模式
*
* @param data 加密后的密文
* @param
* @return 源字符串
* @throws Exception
*/
public static String decrypt(String data, String key, String encoding, String ivParameter, String cipherAlgorithm, String keyAlgorithm) {
try {
String encryptStr = "";
if (StringUtils.isNotEmpty(data)) {
encryptStr = data.replace(" ", "+");
}
byte[] sourceBytes = Base64.decodeBase64(encryptStr);
byte[] keyBytes = key.getBytes(encoding);
Cipher cipher = Cipher.getInstance(cipherAlgorithm, "BC");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes(encoding));
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, keyAlgorithm), iv);
byte[] decoded = cipher.doFinal(sourceBytes);
return new String(decoded, encoding);
} catch (Exception e) {
log.info("------------------->请求加密参数不正确");
log.error(e.toString(), e);
}
return null;
}
}

View File

@ -0,0 +1,68 @@
package com.bonus.common.utils.encryption;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
/**
* @author 黑子
* SM2加解密
*/
public class Sm2Util {
static SM2 sm2 = SmUtil.sm2();
/**
* 获取公钥
* @return
*/
public static String getPublicKey(){
return HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false));
}
/**
* 获取私钥
* @return
*/
public static String getPrivateKey(){
return HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey()));
}
// public static void main(String[] args) {
// String privateKey = getPrivateKey();
// String publicKey = getPublicKey();
// String msg= encrypt(publicKey,"1234567890");
//// String msg2= decrypt(privateKey,msg);
// System.err.println(msg);
//// System.err.println(msg2);
// }
/**
* 加密
*公钥加密
* @param publicKey 公钥
* @param data 明文
* @return 密文
*/
public static String encrypt(String publicKey, String data) {
return SmUtil.sm2(null, publicKey)
.encryptHex(data.getBytes(), KeyType.PublicKey)
// 加密后密文前面会有04需要去掉
.substring(2);
}
/**
* 解密
* 私钥解密
* @param privateKey 私钥
* @param data 密文
* @return 明文
*/
public static String decrypt(String privateKey, String data) {
// 前端加密是没有04的所以解析的时候要加04
data = "04" + data;
return SmUtil.sm2(privateKey, null)
.decryptStr(data, KeyType.PrivateKey);
}
}

View File

@ -0,0 +1,31 @@
package com.bonus.common.utils.encryption;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.digest.SM3;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
/**
* @author weiweiw
* SM3加解密
*/
public class Sm3Util {
static SM3 sm3 = SmUtil.sm3WithSalt("2cc0c5f9f1749f1632efa9f63e902323".getBytes(StandardCharsets.UTF_8));
public static String encrypt(String data) {
return Sm3Util.sm3.digestHex(data);
}
public static String encrypt(InputStream data) {
return Sm3Util.sm3.digestHex(data);
}
public static String encrypt(File dataFile) {
return Sm3Util.sm3.digestHex(dataFile);
}
}

View File

@ -0,0 +1,67 @@
package com.bonus.common.utils.encryption;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.SM4;
/**
* @author bonus
*/
public class Sm4Utils {
/**
* 必须是16字节
*/
private static final String KEY = "78d1295afa99449b99d6f83820e6965c";
private static final String IV = "f555adf6c01d0ab0761e626a2dae34a2";
/**
* 加密数据使用固定盐
*
* @param plainText 明文待加密的字符串
* @return 加密后的密文包含盐Hex 编码格式如果加密异常就返回传入的字符串
*/
public static String encrypt(String plainText) {
try {
SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, HexUtil.decodeHex(KEY),HexUtil.decodeHex(IV));
// 加密带盐的明文
byte[] encryptedData = sm4.encrypt(plainText);
// 返回带盐的加密结果Hex编码
return HexUtil.encodeHexStr(encryptedData);
} catch (Exception e) {
return plainText; // 发生异常时返回传入字符串
}
}
/**
* 解密数据使用固定盐
*
* @param cipherText 密文包含盐Hex 编码格式的字符串
* @return 解密后的明文字符串如果解密异常就返回传入的字符串
*/
public static String decrypt(String cipherText) {
try {
// 初始化SM4解密工具
SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, HexUtil.decodeHex(KEY),HexUtil.decodeHex(IV));
// 解密数据
byte[] decryptedData = sm4.decrypt(cipherText);
return new String(decryptedData);
} catch (Exception e) {
return cipherText; // 发生异常时返回传入字符串
}
}
// 测试方法演示加密和解密过程
public static void main(String[] args) {
String plainText = "15398187429";
System.out.println("原文: " + plainText);
// 加密明文
String encryptedText = Sm4Utils.encrypt(plainText);
System.out.println("加密后: " + encryptedText);
// 解密密文
String decryptedText = Sm4Utils.decrypt(plainText);
System.out.println("解密后: " + decryptedText);
}
}

View File

@ -0,0 +1,70 @@
package com.bonus.common.utils.global;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Author: meng
* @Description: 常用变量
* @Date: 2023/3/30 10:29
* @Version: 1.0
*/
@Component
public class CommonConstant {
/**
* jwt token
*/
public static final String JWT_TOKEN = "jwt-token";
/**
* 请求头中的token
*/
public static final String X_TOKEN = "X-TOKEN";
/**
* 请求头中的sign
*/
public static final String X_SIGN = "X-SIGN";
/**
*
*/
public static final String X_APPID = "X-APPID";
public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";
public static final String CODE = "code";
public static final String MESSAGE = "message";
public static final String MSG = "msg";
public static final String UTF8 = "UTF-8";
public static final String RSA_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFJIl4il6nDBlF/3byWB/KXRqfEXkviz7ZvO7TU7JBfh7sFqfgLtJFDSA33+qTHOtYTCjCrwl6oWWX7Aff39HiFW1IBnhKjYdSK5/8ruQY+Y2xbpBMgslA0m2euOv3XPJUXWh0JGBqPllgzvtbtUA1iBELAHVYBACuQPYP2VcPeQIDAQAB";
public static final String RSA_PRIVATE_KEY = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMUkiXiKXqcMGUX/dvJYH8pdGp8ReS+LPtm87tNTskF+HuwWp+Au0kUNIDff6pMc61hMKMKvCXqhZZfsB9/f0eIVbUgGeEqNh1Irn/yu5Bj5jbFukEyCyUDSbZ646/dc8lRdaHQkYGo+WWDO+1u1QDWIEQsAdVgEAK5A9g/ZVw95AgMBAAECgYABvRrBR2ciTgcDCQfBh2lwXXXYpUzOUIoTXYk1r+1IipY3OtPsND2CgmUgWQc2mPCybKmHXgfVXwsIVfqTzOOK+PEMVGYNflUdXgV3hNffRzl/nfPdpqhb2ALu8ftPwiGq5QN2PqaRgY9kM67Ye/cCjFzm/kLIqsNuXLKiQc1ioQJBAO7g4ZBcG/D0IxtiR4RdXYtr4wQc+cmscSKj5RPNBwn0bh9psOSg2loS/wWUmCnYSncsLGgMzPl+yPkTLwGryH0CQQDTRduiOzu6bFdOw6tI6eOxHB5h0kfcim4VT/Huh5RyP+GC7kLBmknbBO/tQXxSDVaG81Pkr+INHxJmctfKik+tAkEAtBIrl0IIAhRXnp3wYXRsPtxeLkyVc5SdWEqKNen5Y2Sx2tY2dbJXx0zIl3FTXz/fqoRPGUSFA5Kydygh6DWRlQJBAMmOfOHB9tJ8Z7LJ85AFKucdt1KFpW8eVbVZZqq0iAeTMBaULfW7tzgO9sJ3Vh6FgQYP//pNXbA883XvnDUrTKUCQQDgLO7mThmy7iqqo0be4a2ycy9fvORFYzSq1t6mTd+gr73CMCy2bTmyv/Qp4QsuPIKea0iE+HA/la5zlM8eAxOq";
/**
* 公共返回方法
* @param exchange
* @param code
* @param message
* @return
*/
public static Mono<Void> buildResponse(ServerWebExchange exchange, int code, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.OK);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE);
JSONObject jsonObject = new JSONObject();
jsonObject.put(CODE, code);
jsonObject.put(MSG, message);
DataBuffer bodyDataBuffer = response.bufferFactory().wrap(jsonObject.toJSONString().getBytes());
return response.writeWith(Mono.just(bodyDataBuffer));
}
}

View File

@ -0,0 +1,62 @@
package com.bonus.common.utils.global;
/**
* @author 黑子
* 全局变量
*/
public class SystemGlobal {
public final static String TEXT_FIX=".text";
/**
* true
*/
public static String TRUE_STR="true";
/**
* formData
*/
public static String FORM_DATA="formData";
/**
*
* 解密头
*/
public final static String KEY_DECRYPT="decrypt";
/**
* 加密头
*/
public final static String KEY_ENCRYPT ="encryption";
/**
* 成功的200 字符串
*/
public final static String SUCCESS_CODE_STR="200";
/**
* POST 请求
*/
public final static String POST="POST";
/**
* PUT 请求
*/
public final static String PUT="PUT";
/**
* 成功数据数量
*/
public final static int SUCCESS_NUM=1;
/**
* 异常日志
*/
public final static String LOG_ERR="2";
public final static int LOG_DEFEAT_SIZE=1;
public final static String ERR_NUM="NAN";
//
public final static String LOG_TYPE="1";
//身份证识别成功码
public final static String IDCARD_RECORD_SUCCESS="20000";
}

View File

@ -3,6 +3,9 @@ package com.bonus.framework.config;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;
import com.bonus.common.filter.RequestCoverFilter;
import com.bonus.common.filter.ResponseEncryptFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
@ -11,6 +14,7 @@ import org.springframework.context.annotation.Configuration;
import com.bonus.common.filter.RepeatableFilter;
import com.bonus.common.filter.XssFilter;
import com.bonus.common.utils.StringUtils;
import org.springframework.core.Ordered;
/**
* Filter配置
@ -55,4 +59,27 @@ public class FilterConfig
return registration;
}
@Bean
public FilterRegistrationBean<RequestCoverFilter> requestCoverFilterRegistration() {
FilterRegistrationBean<RequestCoverFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new RequestCoverFilter());
registration.addUrlPatterns("/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
registration.setName("requestCoverFilter");
return registration;
}
/**
* 注册响应加密过滤器
*/
@Bean
public FilterRegistrationBean<ResponseEncryptFilter> responseEncryptFilterRegistration() {
FilterRegistrationBean<ResponseEncryptFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new ResponseEncryptFilter());
registration.addUrlPatterns("/*");
registration.setOrder(-5); // 设置过滤器顺序
registration.setName("responseEncryptFilter");
return registration;
}
}

View File

@ -0,0 +1,50 @@
package com.bonus.framework.filter;
import com.bonus.framework.interceptor.ReadHttpRequestWrapper;
import com.bonus.common.utils.global.SystemGlobal;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
*
* @author bonus
*/
@Component
@WebFilter("/*")
@Slf4j
public class MyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
log.debug("进入MyFilter过滤器,parameterMap");
if (SystemGlobal.POST.equals(request.getMethod()) || SystemGlobal.PUT.equals(request.getMethod()) ){
//去除文件上传
String header = request.getHeader(HttpHeaders.CONTENT_TYPE);
if(StringUtils.isNotEmpty(header)){
if(header.contains(MediaType.MULTIPART_FORM_DATA_VALUE)){
filterChain.doFilter(request, response);
return;
}
}
//解决 无法流重复读取的问题
ReadHttpRequestWrapper requestWrapper = new ReadHttpRequestWrapper(request, request.getParameterMap());
filterChain.doFilter(requestWrapper, response);
}else{
filterChain.doFilter(request, response);
}
}
}

View File

@ -0,0 +1,70 @@
package com.bonus.framework.interceptor;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
/**
* 包装和增强原始的 HttpServletRequest 对象允许多次读取请求体body的数据特别是在处理请求时使用流读取数据的情况下
* @author 黑子
*/
public class ReadHttpRequestWrapper extends HttpServletRequestWrapper {
private final String body;
private final Map<String, String[]> parameters;
/**
*
* @param request
*/
public ReadHttpRequestWrapper(HttpServletRequest request,Map<String, String[]> parameters) throws IOException {
super(request);
StringBuilder sb = new StringBuilder();
BufferedReader reader = request.getReader();
String readCount = "";
while((readCount = reader.readLine()) != null){
sb.append(readCount);
}
this.parameters = parameters;
body = sb.toString();
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayIns = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletIns = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(javax.servlet.ReadListener listener) {
}
@Override
public int read() {
return byteArrayIns.read();
}
};
return servletIns;
}
}

View File

@ -0,0 +1,241 @@
package com.bonus.framework.interceptor;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author GeYazhong
* @date 2021/11/23 10:31
*/
@Slf4j
public class XssRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
private String queryString;
private String streamParam;
private boolean checked;
public XssRequestWrapper(HttpServletRequest request) {
super(request);
getParameterMap();
BufferedReader reader;
try {
reader = request.getReader();
StringBuilder sb = new StringBuilder();
char[] buf = new char[1024];
int rd;
while ((rd = reader.read(buf)) != -1) {
sb.append(buf, 0, rd);
}
reader.close();
streamParam = xssClean(sb.toString());
setChecked(xssCleanNew(sb.toString()) && xssCleanNew(request.getQueryString()));
body = streamParam.getBytes();
} catch (IOException e) {
log.error(e.getLocalizedMessage(),e);
}
queryString = xssClean(request.getQueryString());
}
@Override
public String getQueryString() {
return queryString;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
int read = bais.read();
bais.close();
return read;
}
};
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> requestMap = super.getParameterMap();
if(requestMap!=null){
Iterator iterator = requestMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry me = (Map.Entry) iterator.next();
String[] values = (String[]) me.getValue();
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
values[i] = xssClean(values[i]);
}
}
}}
return requestMap;
}
@Override
public String[] getParameterValues(String paramString) {
String[] arrayOfString1 = super.getParameterValues(paramString);
if (arrayOfString1 == null){
return null;
}
int i = arrayOfString1.length;
String[] arrayOfString2 = new String[i];
for (int j = 0; j < i; j++) {
if (arrayOfString1[j] != null) {
arrayOfString2[j] = xssClean(arrayOfString1[j]);
}
}
return arrayOfString2;
}
@Override
public String getParameter(String paramString) {
String str = super.getParameter(paramString);
if (str == null){
return null;
}
return xssClean(str);
}
@Override
public String getHeader(String paramString) {
String str = super.getHeader(paramString);
if (str == null){
return null;
}
return xssClean(str);
}
String regex1="<script>(.*?)</script>";
String regex2="src[\r\n]*=[\r\n]*'(.*?)'";
String regex3="src[\r\n]*=[\r\n]*\"(.*?)\"";
String regex4="</script>";
String regex5="<script(.*?)>";
String regex6="eval\\((.*?)\\)";
String regex7="e-xpression\\((.*?)\\)";
String regex8="javascript:";
String regex9="vbscript:";
String regex10="onload(.*?)=";
private String xssClean(String value) {
if (value == null) {
return value;
}
value = value.replaceAll("", "");
Pattern scriptPattern = Pattern.compile(regex1, Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(regex4, Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(regex5, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(regex6, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(regex7, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(regex8, Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(regex9, Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
return value;
}
private boolean xssCleanNew(String value) {
boolean find = false;
if (value == null) {
return true;
}
List<Pattern> patterns = new ArrayList<Pattern>();
patterns.add(Pattern.compile(regex1, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex4, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex5, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex6, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex7, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex8, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex9, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
for (Pattern pattern : patterns) {
find = match(pattern, value) ;
if (find) {
break;
}
}
return !find;
}
/**
* 执行正则表达式
*
* @param pattern
* 表达式
* @param str
* 待验证字符串
* @return 返回 <b>true </b>,否则为 <b>false </b>
*/
private static boolean match(Pattern pattern, String str) {
Matcher m = pattern.matcher(str);
return m.find();
}
public String getReaderParam() {
return streamParam;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
}

View File

@ -215,7 +215,7 @@ public class GenUtils
*/
public static String replaceText(String text)
{
return RegExUtils.replaceAll(text, "(?:表|若依)", "");
return RegExUtils.replaceAll(text, "(?:表|)", "");
}
/**

View File

@ -10,7 +10,7 @@
<name>bonus</name>
<url>http://www.bonus.vip</url>
<description>若依管理系统</description>
<description>工程档案资料移交管理系统</description>
<properties>
<bonus.version>3.9.0</bonus.version>
@ -218,6 +218,8 @@
<version>${bonus.version}</version>
</dependency>
</dependencies>
</dependencyManagement>