众所周知,http里的请求响应body只能用一次。
所以如果你像我一样,做一些危险操作,进行拦截并且记录成案的话,可能需要做一下修改。
步骤如下: - 对请求进行包装 - 通过过滤器进行包装 - 切面记录在案
对请求进行包装
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private final byte[] cachedBody;
public MultiReadHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
// 将请求体读取并缓存
// 将请求体读取并缓存
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
InputStream requestInputStream = request.getInputStream();
while ((bytesRead = requestInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
this.cachedBody = byteArrayOutputStream.toByteArray();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream(), StandardCharsets.UTF_8));
}
public String getCachedBody() {
return new String(cachedBody, StandardCharsets.UTF_8);
}
}
代码很简单,就是copy了一下,覆写了一下方法,我们取到了数据。
入口
我们要拦截到请求,并且戴上前面这个帽子。
@Component
public class RequestResponseLoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 判断是否为 JSON 请求
String contentType = httpRequest.getContentType();
if (contentType != null && contentType.contains("application/json")) {
// 包装请求以缓存 Body
MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(httpRequest);
// 将包装后的请求传递到下一个过滤器或处理器
chain.doFilter(wrappedRequest, response);
return;
}
}
// 非 JSON 请求直接传递
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
注意,我只拦截是json内容的body,其他的图片我方形。
切面数据获取
切面,我需要获取到用户的ID,请求路径,请求方法,请求参数,调用IP
@Slf4j
@Aspect
@Component
public class LogCut {
@Autowired
private IAdminLogRemote adminLogRemote;
//切入点
@Pointcut("@annotation(sobLogRecord)")
public void checkPermissionPointcut(SobLogRecord sobLogRecord) {
}
@After("checkPermissionPointcut(sobLogRecord)")
public void beforeMethod(JoinPoint joinPoint, SobLogRecord sobLogRecord) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
try {
HttpServletRequest request = attributes.getRequest();
String requestMethod = request.getMethod();
if ("OPTIONS".equals(requestMethod)) {
return;
}
//检查当前用户是否有
UserVo userVo = AdminVoContext.get();
log.info("log cut...");
if (userVo == null) {
//没有登录
throw new ForbiddenException("账号未登录.");
}
AdminLog adminLog = new AdminLog();
//获取描述
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
if (method.isAnnotationPresent(ApiOperation.class)) {
ApiOperation operation = method.getAnnotation(ApiOperation.class);
String value = operation.value();
adminLog.setDescription(value);
}
StringBuilder stringBuilder = new StringBuilder();
String queryString = request.getQueryString();
if (!TextUtils.isEmpty(queryString)) {
stringBuilder.append("queryString:").append(queryString).append(",");
}
if (request instanceof MultiReadHttpServletRequest) {
MultiReadHttpServletRequest multiReadRequest = (MultiReadHttpServletRequest) request;
String requestBody = multiReadRequest.getCachedBody();
log.info("Request Body: " + requestBody);
if (!TextUtils.isEmpty(requestBody)) {
stringBuilder.append("bodyStr:").append(requestBody);
}
}
String ip = IpUtils.getRealIpWithNginxProxy(request);
adminLog.setAdminId(userVo.getId());
adminLog.setIp(ip);
log.info("request.getRequestURI() ==> " + request.getRequestURI());
adminLog.setApi(request.getMethod() + ":" + request.getRequestURI());
adminLog.setParamsString(stringBuilder.toString());
adminLogRemote.postAdminLog(adminLog);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
在适当的接口加上注解,这样我就可以完成了对危险操作的记录了。