diff --git a/src/main/java/com/dispose/PhoenixBootApplication.java b/src/main/java/com/dispose/PhoenixBootApplication.java index e83217df..8d432bea 100644 --- a/src/main/java/com/dispose/PhoenixBootApplication.java +++ b/src/main/java/com/dispose/PhoenixBootApplication.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; @@ -21,6 +22,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableAspectJAutoProxy @EnableTransactionManagement @EnableEncryptableProperties +@ServletComponentScan @MapperScan(basePackages = {"com.dispose.mapper"}) @Slf4j public class PhoenixBootApplication { diff --git a/src/main/java/com/dispose/common/Helper.java b/src/main/java/com/dispose/common/Helper.java index f92a11de..99d28755 100644 --- a/src/main/java/com/dispose/common/Helper.java +++ b/src/main/java/com/dispose/common/Helper.java @@ -5,6 +5,11 @@ import inet.ipaddr.IPAddress; import inet.ipaddr.IPAddressSeqRange; import inet.ipaddr.IPAddressString; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.sql.Timestamp; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -169,4 +174,25 @@ public class Helper { return false; } + + /** + * Input stream 2 string string. + * + * @param inputStream the input stream + * @return the string + */ + public static String inputStream2String(InputStream inputStream) { + StringBuilder sb = new StringBuilder(); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()))) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + return sb.toString(); + } } diff --git a/src/main/java/com/dispose/controller/logging/RestfulLog.java b/src/main/java/com/dispose/controller/logging/RestfulLog.java index 73d01caf..3f2d1fd0 100644 --- a/src/main/java/com/dispose/controller/logging/RestfulLog.java +++ b/src/main/java/com/dispose/controller/logging/RestfulLog.java @@ -88,7 +88,7 @@ public class RestfulLog { } if (requestMap.containsKey(attributes.hashCode())) { - log.debug("Interface [{}] request <{}> from {}, token = <{}>\n" + + log.info("Interface [{}] request <{}> from {}, token = <{}>\n" + "+++ Request: {}\n" + "--- Response: {}", reqType, reqPath, reqIp, reqToken, requestMap.get(attributes.hashCode()), @@ -118,7 +118,7 @@ public class RestfulLog { } if (requestMap.containsKey(attributes.hashCode())) { - log.debug("Interface [{}] request <{}> from {}, token = <{}>\n" + + log.info("Interface [{}] request <{}> from {}, token = <{}>\n" + "+++ Request: {}\n" + "--- Response Exception: {}", reqType, reqPath, reqIp, reqToken, requestMap.get(attributes.hashCode()), ex.getMessage()); diff --git a/src/main/java/com/dispose/exception/GlobalExceptionHandler.java b/src/main/java/com/dispose/exception/GlobalExceptionHandler.java index 097af812..653218c6 100644 --- a/src/main/java/com/dispose/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/dispose/exception/GlobalExceptionHandler.java @@ -1,6 +1,8 @@ package com.dispose.exception; +import com.dispose.common.ConstValue; import com.dispose.common.ErrorCode; +import com.dispose.common.Helper; import com.dispose.pojo.dto.protocol.base.BaseRespStatus; import com.dispose.pojo.dto.protocol.base.ProtocolRespDTO; import com.dispose.security.exception.SecurityProtocolException; @@ -10,6 +12,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; @@ -23,26 +26,44 @@ import java.util.concurrent.atomic.AtomicInteger; @ControllerAdvice @Slf4j public class GlobalExceptionHandler { - /** * Handle exception protocol resp dto. * * @param rsp the rsp + * @param req the req * @param ex the ex * @return the protocol resp dto */ @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody public ProtocolRespDTO handleException(HttpServletResponse rsp, + HttpServletRequest req, MethodArgumentNotValidException ex) { + try { + String reqType = req.getMethod(); + String reqPath = req.getRequestURI(); + String reqIp = req.getRemoteAddr(); + String reqToken = req.getHeader("Authorization"); + if (reqToken != null && reqToken.length() > 0) { + reqToken = reqToken.replace(ConstValue.STRING_HTTP_AUTH_HEAD, ""); + } + log.info("Interface [{}] request <{}> from {}, token = <{}>\n" + + "+++ Request: {}\n" + + "--- Verify params failed: {}", + reqType, reqPath, reqIp, reqToken, Helper.inputStream2String(req.getInputStream()), + ex.getMessage()); + } catch (Exception ignored) { + } + log.debug("Argument Exception: ", ex); + List exMsg = new ArrayList<>(); AtomicInteger idx = new AtomicInteger(); ex.getBindingResult() - .getAllErrors() - .forEach(v -> exMsg.add(idx.getAndIncrement() + ": " + v.getDefaultMessage())); + .getAllErrors() + .forEach(v -> exMsg.add(idx.getAndIncrement() + ": " + v.getDefaultMessage())); rsp.setStatus(ErrorCode.ERR_PARAMEXCEPTION.getHttpCode()); @@ -51,6 +72,54 @@ public class GlobalExceptionHandler { exMsg.toArray(new String[0])); } + /** + * Handle exception protocol resp dto. + * + * @param rsp the rsp + * @param req the req + * @param ex the ex + * @return the protocol resp dto + */ + @ExceptionHandler(SecurityProtocolException.class) + @ResponseBody + public ProtocolRespDTO handleException(HttpServletResponse rsp, + HttpServletRequest req, + SecurityProtocolException ex) { + try { + String reqType = req.getMethod(); + String reqPath = req.getRequestURI(); + String reqIp = req.getRemoteAddr(); + String reqToken = req.getHeader("Authorization"); + if (reqToken != null && reqToken.length() > 0) { + reqToken = reqToken.replace(ConstValue.STRING_HTTP_AUTH_HEAD, ""); + } + log.info("Interface [{}] request <{}> from {}, token = <{}>\n" + + "+++ Request: {}\n" + + "--- Verify params failed: {}", + reqType, reqPath, reqIp, reqToken, Helper.inputStream2String(req.getInputStream()), + ex.getMessage()); + } catch (Exception ignored) { + } + log.debug("SecurityProtocolException Exception: ", ex); + + List errMeg = new ArrayList<>(); + rsp.setStatus(ErrorCode.ERR_PARAMEXCEPTION.getHttpCode()); + + errMeg.add(ex.getErr().getMsg()); + + if (ex.getDescription() != null && ex.getDescription().length() > 0) { + errMeg.add(ex.getDescription()); + } + + if (ex.getMessage() != null && ex.getMessage().length() > 0) { + errMeg.add(ex.getMessage()); + } + + return ProtocolRespDTO.result(ErrorCode.ERR_PARAMEXCEPTION, + ErrorCode.ERR_PARAMEXCEPTION.getHttpCode(), + errMeg.toArray(new String[0])); + } + /** * Handle exception protocol resp dto. * @@ -61,22 +130,23 @@ public class GlobalExceptionHandler { @ExceptionHandler(Throwable.class) @ResponseBody public ProtocolRespDTO handleException(HttpServletResponse rsp, Throwable ex) { + List errMeg = new ArrayList<>(); log.debug("Throwable Exception: ", ex); rsp.setStatus(ErrorCode.ERR_PARAMEXCEPTION.getHttpCode()); - if (ex instanceof SecurityProtocolException) { - return ProtocolRespDTO.result(ErrorCode.ERR_PARAMEXCEPTION, - ErrorCode.ERR_PARAMEXCEPTION.getHttpCode(), - new String[]{((SecurityProtocolException) ex).getErr().getMsg(), ex.getMessage()}); - } else if (ex instanceof ControllerNotSupportException) { - return ProtocolRespDTO.result(ErrorCode.ERR_PARAMEXCEPTION, - ErrorCode.ERR_PARAMEXCEPTION.getHttpCode(), - new String[]{((ControllerNotSupportException) ex).getErr().getMsg(), ex.getMessage()}); + if (ex instanceof ControllerNotSupportException) { + errMeg.add(((ControllerNotSupportException) ex).getErr().getMsg()); } else { - return ProtocolRespDTO.result(ErrorCode.ERR_PARAMEXCEPTION, - ErrorCode.ERR_PARAMEXCEPTION.getHttpCode(), - new String[]{ErrorCode.ERR_PARAMEXCEPTION.getMsg(), ex.getMessage()}); + errMeg.add(ErrorCode.ERR_PARAMEXCEPTION.getMsg()); } + + if (ex.getMessage() != null && ex.getMessage().length() > 0) { + errMeg.add(ex.getMessage()); + } + + return ProtocolRespDTO.result(ErrorCode.ERR_PARAMEXCEPTION, + ErrorCode.ERR_PARAMEXCEPTION.getHttpCode(), + errMeg.toArray(new String[0])); } } diff --git a/src/main/java/com/dispose/interceptor/RequestBodyCacheWrapper.java b/src/main/java/com/dispose/interceptor/RequestBodyCacheWrapper.java new file mode 100644 index 00000000..d34b964c --- /dev/null +++ b/src/main/java/com/dispose/interceptor/RequestBodyCacheWrapper.java @@ -0,0 +1,106 @@ +package com.dispose.interceptor; + +import com.dispose.common.Helper; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +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.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +/** + * The type Request body cache wrapper. + * + * @author + */ +public class RequestBodyCacheWrapper extends HttpServletRequestWrapper { + /** + * The Body. + */ + private final byte[] body; + + /** + * Instantiates a new Request body cache wrapper. + * + * @param request the request + */ + public RequestBodyCacheWrapper(HttpServletRequest request) { + super(request); + // 将body数据存储起来 + String bodyStr = getBodyString(request); + body = bodyStr.getBytes(Charset.defaultCharset()); + } + + /** + * Gets body string. + * + * @param request the request + * @return the body string + */ + public String getBodyString(final ServletRequest request) { + try { + return Helper.inputStream2String(request.getInputStream()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Gets body string. + * + * @return the body string + */ + public String getBodyString() { + final InputStream inputStream = new ByteArrayInputStream(body); + + return Helper.inputStream2String(inputStream); + } + + /** + * Gets reader. + * + * @return the reader + */ + @Override + public BufferedReader getReader() { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + /** + * Gets input stream. + * + * @return the input stream + */ + @Override + public ServletInputStream getInputStream() { + + final ByteArrayInputStream inputStream = new ByteArrayInputStream(body); + + return new ServletInputStream() { + @Override + public int read() { + return inputStream.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + }; + } +} diff --git a/src/main/java/com/dispose/interceptor/RequestBodyFilter.java b/src/main/java/com/dispose/interceptor/RequestBodyFilter.java new file mode 100644 index 00000000..da38df81 --- /dev/null +++ b/src/main/java/com/dispose/interceptor/RequestBodyFilter.java @@ -0,0 +1,51 @@ +package com.dispose.interceptor; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * The type Request body filter. + * + * @author + */ + +@WebFilter(filterName = "RequestBodyFilter", urlPatterns = "/*") +public class RequestBodyFilter implements Filter { + /** + * Init. + * + * @param filterConfig the filter config + */ + @Override + public void init(FilterConfig filterConfig) { + } + + /** + * Destroy. + */ + @Override + public void destroy() { + } + + /** + * Do filter. + * + * @param request the request + * @param response the response + * @param filterChain the filter chain + * @throws IOException the io exception + * @throws ServletException the servlet exception + */ + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { + ServletRequest requestWrapper = new RequestBodyCacheWrapper((HttpServletRequest) request); + filterChain.doFilter(requestWrapper, response); + } +} diff --git a/src/main/java/com/dispose/pojo/dto/protocol/base/BaseProtocolDTO.java b/src/main/java/com/dispose/pojo/dto/protocol/base/BaseProtocolDTO.java index c36bf1b3..106f9950 100644 --- a/src/main/java/com/dispose/pojo/dto/protocol/base/BaseProtocolDTO.java +++ b/src/main/java/com/dispose/pojo/dto/protocol/base/BaseProtocolDTO.java @@ -41,8 +41,8 @@ public class BaseProtocolDTO { allowableValues = "0, 1, 2", example = "0") @NotNull(message = "cryptoType 字段不能为空", groups = ValidGroups.ProtocolCommonValid.class) - @Range(min = 0, max = 2, - message = "cryptoType 字段取值为 [0, 2]" + @Range(min = 0, max = 4, + message = "cryptoType 字段取值为 [0, 4]" , groups = ValidGroups.ProtocolCommonValid.class) private Integer cryptoType; diff --git a/src/main/java/com/dispose/security/exception/SecurityProtocolException.java b/src/main/java/com/dispose/security/exception/SecurityProtocolException.java index 051a88a3..53d34626 100644 --- a/src/main/java/com/dispose/security/exception/SecurityProtocolException.java +++ b/src/main/java/com/dispose/security/exception/SecurityProtocolException.java @@ -17,6 +17,8 @@ public class SecurityProtocolException extends RuntimeException { */ private ErrorCode err; + private String description; + /** * Instantiates a new Security exception. * @@ -25,5 +27,12 @@ public class SecurityProtocolException extends RuntimeException { public SecurityProtocolException(ErrorCode err) { super(); this.err = err; + this.description = null; + } + + public SecurityProtocolException(ErrorCode err, String readme) { + super(); + this.err = err; + this.description = readme; } } diff --git a/src/main/java/com/dispose/service/impl/ProtocolSecurityServiceImpl.java b/src/main/java/com/dispose/service/impl/ProtocolSecurityServiceImpl.java index 91ef78ce..bae6afea 100644 --- a/src/main/java/com/dispose/service/impl/ProtocolSecurityServiceImpl.java +++ b/src/main/java/com/dispose/service/impl/ProtocolSecurityServiceImpl.java @@ -56,6 +56,10 @@ public class ProtocolSecurityServiceImpl implements ProtocolSecurityService { return ciphertext; } + if (cryptoType > ProtoCryptoType.CRYPTO_AES256.getCode() || cryptoType < ProtoCryptoType.CRYPTO_NONE.getCode()) { + throw new SecurityProtocolException(ErrorCode.ERR_PARAMS, "cryptoType 字段取值为 [0, 4]"); + } + // 反序列化为对象处理 ProtocolReqDTO proReq = objectMapper .readValue(ciphertext, new TypeReference>() {