diff --git a/policeSecurityServer/src/main/java/com/changhu/pojo/dto/SecurityUnitUseStatisticsDTO.java b/policeSecurityServer/src/main/java/com/changhu/pojo/dto/SecurityUnitUseStatisticsDTO.java index 738f06d..185ba4b 100644 --- a/policeSecurityServer/src/main/java/com/changhu/pojo/dto/SecurityUnitUseStatisticsDTO.java +++ b/policeSecurityServer/src/main/java/com/changhu/pojo/dto/SecurityUnitUseStatisticsDTO.java @@ -16,6 +16,8 @@ import lombok.Data; public class SecurityUnitUseStatisticsDTO { @Schema(description = "服务项目id") private Long serviceProjectId; + @Schema(description = "公安单位id") + private Long policeUnitId; @Schema(description = "公安单位名称") private String policeUnitName; @Schema(description = "保安单位名称") diff --git a/policeSecurityServer/src/main/java/com/changhu/support/filter/BodyWrapperFilter.java b/policeSecurityServer/src/main/java/com/changhu/support/filter/BodyWrapperFilter.java new file mode 100644 index 0000000..2acfb14 --- /dev/null +++ b/policeSecurityServer/src/main/java/com/changhu/support/filter/BodyWrapperFilter.java @@ -0,0 +1,29 @@ +package com.changhu.support.filter; + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; + +/** + * @author 20252 + * @createTime 2024/11/19 下午3:07 + * @desc BodyWrapperFilter... + */ +@Slf4j +public class BodyWrapperFilter implements Filter { + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + ServletRequest requestWrapper = null; + if (servletRequest instanceof HttpServletRequest) { + requestWrapper = new CustomHttpServletRequestWrapper((HttpServletRequest) servletRequest); + } + if (requestWrapper == null) { + filterChain.doFilter(servletRequest, servletResponse); + } else { + filterChain.doFilter(requestWrapper, servletResponse); + } + + } +} diff --git a/policeSecurityServer/src/main/java/com/changhu/support/filter/CustomHttpServletRequestWrapper.java b/policeSecurityServer/src/main/java/com/changhu/support/filter/CustomHttpServletRequestWrapper.java new file mode 100644 index 0000000..a4c11ba --- /dev/null +++ b/policeSecurityServer/src/main/java/com/changhu/support/filter/CustomHttpServletRequestWrapper.java @@ -0,0 +1,65 @@ +package com.changhu.support.filter; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +/** + * @author 20252 + * @createTime 2024/11/19 下午3:12 + * @desc CustomHttpServletRequestWrapper... + */ +public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper { + private final byte[] body; + + public CustomHttpServletRequestWrapper(HttpServletRequest request) throws IOException { + super(request); + BufferedReader reader = request.getReader(); + try (StringWriter writer = new StringWriter()) { + int read; + char[] buf = new char[1024 * 8]; + while ((read = reader.read(buf)) != -1) { + writer.write(buf, 0, read); + } + this.body = writer.getBuffer().toString().getBytes(); + } + } + + public String getBody() { + return new String(body, StandardCharsets.UTF_8); + } + + @Override + public ServletInputStream getInputStream() { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + + @Override + public int read() { + return byteArrayInputStream.read(); + } + }; + } + + @Override + public BufferedReader getReader() { + return new BufferedReader(new InputStreamReader(this.getInputStream())); + } +} diff --git a/policeSecurityServer/src/main/java/com/changhu/support/interceptor/OpenApiInterceptor.java b/policeSecurityServer/src/main/java/com/changhu/support/interceptor/OpenApiInterceptor.java index e0cc567..99340ff 100644 --- a/policeSecurityServer/src/main/java/com/changhu/support/interceptor/OpenApiInterceptor.java +++ b/policeSecurityServer/src/main/java/com/changhu/support/interceptor/OpenApiInterceptor.java @@ -1,5 +1,6 @@ package com.changhu.support.interceptor; +import cn.hutool.core.util.StrUtil; import com.changhu.common.annotation.CheckOpenApi; import com.changhu.common.exception.MessageException; import jakarta.servlet.http.HttpServletRequest; @@ -23,6 +24,9 @@ public class OpenApiInterceptor implements HandlerInterceptor { @Override public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) { String header = request.getHeader("X-API-KEY"); + if (StrUtil.isBlank(header)) { + throw new MessageException("请求头缺失"); + } log.info("apiKey:{} {} 请求:{}", header, LocalDateTime.now(), request.getRequestURI()); if (handler instanceof HandlerMethod handlerMethod) { CheckOpenApi methodAnnotation = handlerMethod.getMethodAnnotation(CheckOpenApi.class); diff --git a/policeSecurityServer/src/main/java/com/changhu/support/interceptor/SignInterceptor.java b/policeSecurityServer/src/main/java/com/changhu/support/interceptor/SignInterceptor.java new file mode 100644 index 0000000..4e78e36 --- /dev/null +++ b/policeSecurityServer/src/main/java/com/changhu/support/interceptor/SignInterceptor.java @@ -0,0 +1,122 @@ +package com.changhu.support.interceptor; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.crypto.digest.MD5; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.TypeReference; +import com.baomidou.mybatisplus.extension.toolkit.Db; +import com.changhu.common.db.enums.IsEnable; +import com.changhu.common.exception.MessageException; +import com.changhu.common.utils.IpUtil; +import com.changhu.pojo.entity.AccessKeys; +import com.changhu.support.filter.CustomHttpServletRequestWrapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.*; + +/** + * @author 20252 + * @createTime 2024/11/19 下午1:58 + * @desc SignInterceptor... + */ +@Slf4j +public class SignInterceptor implements HandlerInterceptor { + + private static final String ACCESS_KEY = "access-key";//调用者身份唯一标识 + private static final String TIMESTAMP = "time-stamp";//时间戳 + private static final String SIGN = "sign";//签名 + + @Override + public boolean preHandle(@NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, + @NotNull Object handler) throws Exception { + String ip = IpUtil.getIp(request); + try { + checkSign(request); + } catch (MessageException e) { + log.error("开放接口访问失败:{} 访问时间:{} IP:{} 访问接口:{} ", e.getMessage(), LocalDateTime.now(), ip, request.getRequestURI()); + throw e; + } + + + return true; + } + + private void checkSign(HttpServletRequest request) throws MessageException { + + String accessKey = request.getHeader(ACCESS_KEY); + String timestamp = request.getHeader(TIMESTAMP); + String sign = request.getHeader(SIGN); + + if (StrUtil.isBlank(accessKey) || StrUtil.isBlank(timestamp) || StrUtil.isBlank(sign)) { + throw new MessageException("请求体缺失"); + } + + AccessKeys accessKeyEntity = Db.lambdaQuery(AccessKeys.class) + .eq(AccessKeys::getAccessKey, accessKey) + .oneOpt() + .orElseThrow(() -> new MessageException("无效的accessKey")); + + if (IsEnable.FALSE.equals(accessKeyEntity.getIsEnable())) { + throw new MessageException("无效的accessKey"); + } + + List allowedResources = Optional.ofNullable(accessKeyEntity.getAllowedResources()).orElseThrow(() -> new MessageException("暂无允许访问的资源")); + if (!allowedResources.contains(request.getRequestURI())) { + throw new MessageException("无效的请求资源"); + } + + Map hashMap = new HashMap<>(); + //添加请求url参数 + Map map = HttpUtil.decodeParamMap(request.getQueryString(), StandardCharsets.UTF_8); + if (!map.isEmpty()) { + hashMap.putAll(map); + } + + hashMap.put(ACCESS_KEY, accessKey); + hashMap.put(TIMESTAMP, timestamp); + //添加body参数 + CustomHttpServletRequestWrapper c = (CustomHttpServletRequestWrapper) request; + Optional.ofNullable(new TypeReference>() { + }.parseObject(c.getBody())).ifPresent(hashMap::putAll); + + String nowSign = generatedSign(hashMap, accessKeyEntity.getSecretKey()); + if (!sign.equals(nowSign)) { + throw new MessageException("签名错误"); + } + } + + /** + * 获取签名 + * + * @param map 参数结果 + * @param secretKey 密钥 + * @return 签名字符串 + */ + private String generatedSign(Map map, String secretKey) { + List> infoIds = new ArrayList<>(map.entrySet()); + infoIds.sort(Map.Entry.comparingByKey()); + + StringBuilder sb = new StringBuilder(); + for (Map.Entry m : infoIds) { + if (null == m.getValue() || StrUtil.isNotBlank(m.getValue().toString())) { + sb.append(m.getKey()).append("=").append(URLUtil.encodeAll(m.getValue().toString())).append("&"); + } + } + sb.append("secret-key=").append(secretKey); + return MD5.create().digestHex(sb.toString()).toUpperCase(); + } + + public static void main(String[] args) { + String str1 = "access-key=w2wzi0wefmmo6s735z2el8tfzitya5gj&addr=%E6%B9%96%E5%8D%97%E7%9C%81%E9%95%BF%E6%B2%99%E5%B8%82&age=14&name=zhangsan&time-stamp=1732067854476&secret-key=db1b5214-02ee-497f-957c-88323b4351bf"; + String str2 = "access-key=w2wzi0wefmmo6s735z2el8tfzitya5gj&addr=%E6%B9%96%E5%8D%97%E7%9C%81%E9%95%BF%E6%B2%99%E5%B8%82&age=14&name=zhangsan&time-stamp=1732067854476&secret-key=db1b5214-02ee-497f-957c-88323b4351bf"; + } + +} diff --git a/policeSecurityServer/src/main/resources/mapper/OpenApiMapper.xml b/policeSecurityServer/src/main/resources/mapper/OpenApiMapper.xml index a792d43..645e7cb 100644 --- a/policeSecurityServer/src/main/resources/mapper/OpenApiMapper.xml +++ b/policeSecurityServer/src/main/resources/mapper/OpenApiMapper.xml @@ -37,6 +37,7 @@