package com.zswl.dataservice.service.openApi; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.zswl.dataservice.auth.OpenAPIContext; import com.zswl.dataservice.dao.openApi.BlackListDao; import com.zswl.dataservice.dao.openApi.OpenApiRequestLogDao; import com.zswl.dataservice.dao.openApi.OpenApiSignInfoDao; import com.zswl.dataservice.dataConfig.OpenAPIConfig; import com.zswl.dataservice.domain.openApi.OpenApiRequestLog; import com.zswl.dataservice.domain.openApi.OpenApiSignInfo; import com.zswl.dataservice.service.base.RedisService; import com.zswl.dataservice.service.user.OperationLogsService; import com.zswl.dataservice.type.OperationLogType; import com.zswl.dataservice.utils.AesUtils; import com.zswl.dataservice.utils.DateUtils; import com.zswl.dataservice.utils.HttpUtils; import com.zswl.dataservice.utils.mqtt.type.LogsLevel; import com.zswl.dataservice.utils.net.IPUtil; import com.zswl.dataservice.utils.os.SystemUtil; import com.zswl.dataservice.utils.result.ResultContent; import jakarta.servlet.ServletInputStream; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.Data; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.aspectj.apache.bcel.classfile.Module; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.util.Date; import java.util.HashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * @author TRX * @date 2024/9/11 */ @Slf4j @Service public class OpenApiVerifyService { @Autowired private RedisService redisService; @Autowired private OpenApiRequestLogsService openApiRequestLogsService; @Autowired private OpenApiRequestLogDao openApiRequestLogDao; @Autowired OperationLogsService operationLogsService; @Autowired BlackListDao blackListDao; @Autowired OpenApiSignInfoDao openApiSignInfoDao; //线程池 ExecutorService executorService = Executors.newFixedThreadPool(SystemUtil.getCpuCoreCount() * 2); @Autowired private void init(ApplicationContext applicationContext) { Runtime.getRuntime().addShutdownHook(new Thread(() -> { executorService.shutdownNow(); })); } /** * 验证OpenAPI preHandle * * @param request * @param response * @param object * @return */ @SneakyThrows public boolean verifyOpenAPI(HttpServletRequest request, HttpServletResponse response, Object object) { log.info("---------------------openAPI验证----------------------"); OpenAPIContext.setTime(System.currentTimeMillis()); if (request instanceof ContentCachingRequestWrapper) { ContentCachingRequestWrapper contentCachingRequestWrapper = (ContentCachingRequestWrapper) request; ContentCachingResponseWrapper responseWrapper = (ContentCachingResponseWrapper) response; String requestStr = contentCachingRequestWrapper.getContentAsString(); String msg = "认证错误"; int code = 403; boolean isSuccess = false; if (contentCachingRequestWrapper.getContentLength() > OpenAPIConfig.maxLen) { setResponse(request, responseWrapper, "请求内容过大", 403, false); return false; } log.info("请求数据requestStr: {}", requestStr); String authorization = request.getHeader("authorization"); log.info("authorization数据 {}", authorization); if (StringUtils.isNotEmpty(authorization)) { if (authorization.length() > OpenAPIConfig.maxLen) { setResponse(request, responseWrapper, "请求内容过大", 403, false); return false; } authorization = authorization.replaceAll(" ", ""); if (authorization.startsWith(OpenAPIConfig.OPENBODYSIG)) { authorization = authorization.replace(OpenAPIConfig.OPENBODYSIG, ""); HashMap map = new HashMap<>(); String[] arr = authorization.split(","); isSuccess = true; if (arr.length > 0) { for (String s : arr) { for (String key : OpenAPIConfig.authKeys) { if (s.startsWith(key)) { String val = s.replace(key + "=", ""); val = val.replace("\"", ""); map.put(key, val); break; } } } } if (isSuccess) { OpenApiSignInfo signInfo = null; if (!map.containsKey("AppId")) { isSuccess = false; msg = "没有AppId信息"; } String appId = map.get("AppId"); signInfo = openApiSignInfoDao.findTopByAppId(appId); if (ObjectUtils.isEmpty(signInfo)) { isSuccess = false; msg = "AppId错误"; } if (isSuccess && !map.containsKey("Timestamp")) { isSuccess = false; msg = "没有Timestamp信息"; } String timestamp = map.get("Timestamp"); if (StringUtils.isEmpty(timestamp) || timestamp.length() != 14) { isSuccess = false; msg = "Timestamp格式错误"; } if (isSuccess){ Long time = DateUtils.timeToLong(timestamp, DateUtils.unionAuth); if (time == null || Math.abs(time - System.currentTimeMillis()) > OpenAPIConfig.timeBetween) { isSuccess = false; msg = "Timestamp不符合要求"; } } if (isSuccess && !map.containsKey("Nonce")) { isSuccess = false; msg = "没有Nonce信息"; } String nonce = map.get("Nonce"); if (isSuccess && (StringUtils.isEmpty(nonce) || nonce.length() > 50)) { isSuccess = false; msg = "Nonce为空或长度不符合要求"; } if (isSuccess && !map.containsKey("Signature")) { isSuccess = false; msg = "没有Signature信息"; } String signature = map.get("Signature"); if (isSuccess && (StringUtils.isEmpty(signature) || signature.length() > 200)) { isSuccess = false; msg = "Signature为空或长度不符合要求"; } if(isSuccess) { String sign = AesUtils.signData(requestStr); log.info("数据sign {}", sign); String appKey = signInfo.getAppKey(); String c = String.format("%s%s%s%s", appId, timestamp, nonce, sign); String tempSignature = AesUtils.signMacSHA256(c, appKey); log.info("系统tempSignature {}", tempSignature); log.info("传入signature {}", signature); if (!tempSignature.equals(signature)) { isSuccess = false; msg = "Signature认证错误"; } } } } else { msg = "报文头没有OPEN-BODY-SIG标记"; } } else { msg = "报文头authorization为空"; } if (!isSuccess) { // 验证不成功 setResponse(request, responseWrapper, msg, code, true); return false; } } return true; } /** * 记录日志 postHandle * * @param request * @param response * @param handler * @param modelAndView */ @SneakyThrows public void saveOpenAPILog(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { ResultContent resultContent = null; if (response instanceof ContentCachingResponseWrapper) { ContentCachingResponseWrapper responseWrapper = (ContentCachingResponseWrapper) response; byte[] bytes = responseWrapper.getContentAsByteArray(); resultContent = HttpUtils.toBean(new String(bytes), ResultContent.class); responseWrapper.copyBodyToResponse(); } saveLog(request, resultContent); } public boolean isInBlackList(HttpServletRequest request) { String ip = IPUtil.getRemoteIp(request); if (blackListDao.existsByIp(ip)) { ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);//设置子线程共享 executorService.execute(() -> { operationLogsService.addLogs(String.format("黑名单地址访问: %s", ip), LogsLevel.High, OperationLogType.Black, null); }); return true; } return false; } @SneakyThrows private void setResponse(HttpServletRequest request, ContentCachingResponseWrapper responseWrapper, String msg, int code, boolean isSaveLog) { ResultContent resultContent = ResultContent.buildFail(msg, code); responseWrapper.setCharacterEncoding("UTF-8"); responseWrapper.setHeader("Content-Type", "application/json"); responseWrapper.setStatus(HttpStatus.PAYMENT_REQUIRED.value()); PrintWriter printWriter = responseWrapper.getWriter(); printWriter.write(JSONUtil.toJsonStr(resultContent)); printWriter.flush(); printWriter.close(); responseWrapper.copyBodyToResponse(); if (isSaveLog) { saveLog(request, resultContent); } resultContent = null; } private void saveLog(HttpServletRequest request, ResultContent resultContent) { OpenApiRequestLog openApiRequestLog = new OpenApiRequestLog(); JSONObject param = HttpUtils.getRequestObj(request); if (param != null) { openApiRequestLog.setRequestBody(param); openApiRequestLog.setAppId(param.getStr("appId")); } Long startTime = OpenAPIContext.getTime(); openApiRequestLog.setResponseTime(System.currentTimeMillis()); if (startTime != null) { openApiRequestLog.setRequestTime(startTime); openApiRequestLog.setRequestTimeStr(DateUtils.paresTime(startTime, DateUtils.FORMAT_LONG)); openApiRequestLog.setUserTime(System.currentTimeMillis() - startTime); } try { openApiRequestLog.setUa(request.getHeader("User-Agent")); openApiRequestLog.setIp(IPUtil.getRemoteIp(request)); openApiRequestLog.setUrl(request.getRequestURI()); } catch (Exception e) { e.printStackTrace(); } openApiRequestLog.setAuthorization(request.getHeader("authorization")); openApiRequestLog.setTTL(new Date(System.currentTimeMillis() + OpenAPIConfig.logTTL)); if (resultContent != null) { openApiRequestLog.setIsSuccess(resultContent.isSuccess()); openApiRequestLog.setErrorMsg(resultContent.getMsg()); } openApiRequestLog.setResponseBody(resultContent); openApiRequestLogDao.save(openApiRequestLog); } }