feat: 新增接口日志上传拦截器

11111
winter 9 months ago
parent ea5c6fcb81
commit b53fe0f7d6

@ -0,0 +1,54 @@
package cn.teammodel.config;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* request inputstream controller
*/
public class BodyReaderRequestWrapper extends HttpServletRequestWrapper {
// 将流中的内容保存
private final byte[] buff;
public BodyReaderRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
InputStream is = request.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
baos.write(b, 0, len);
}
buff = baos.toByteArray();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(buff);
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 {
return bais.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
public String getRequestBody() {
return new String(buff);
}
}

@ -0,0 +1,41 @@
package cn.teammodel.config;
import cn.teammodel.config.intercepter.EnhanceRequestServletFilter;
import cn.teammodel.config.intercepter.UploadApiLogInterceptor;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* WebMvc :
* @author winter
* @create 2024-01-15 17:06
*/
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final UploadApiLogInterceptor uploadApiLogInterceptor;
/**
*
*/
@Override
public void addInterceptors(@NotNull InterceptorRegistry registry) {
registry.addInterceptor(uploadApiLogInterceptor);
}
// 注册过滤器
@Bean
public FilterRegistrationBean<EnhanceRequestServletFilter> filterRegistrationBean(){
FilterRegistrationBean<EnhanceRequestServletFilter> filterRegistrationBean=new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new EnhanceRequestServletFilter());
filterRegistrationBean.addUrlPatterns("/*");
//order的数值越小 则优先级越高,这里直接使用的最高优先级
filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return filterRegistrationBean;
}
}

@ -0,0 +1,19 @@
package cn.teammodel.config.intercepter;
import cn.teammodel.config.BodyReaderRequestWrapper;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class EnhanceRequestServletFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 防止流读取一次后就没有了, 所以需要将流继续写出去
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
// 这里将原始request传入读出流并存储
ServletRequest requestWrapper = new BodyReaderRequestWrapper(httpServletRequest);
// 这里将原始request替换为包装后的request此后所有进入controller的request均为包装后的
filterChain.doFilter(requestWrapper, servletResponse);//
}
}

@ -0,0 +1,134 @@
package cn.teammodel.config.intercepter;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.jwt.JWTUtil;
import cn.teammodel.manager.NotificationService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.List;
/**
*
* @author winter
* @create 2024-03-18 10:00
*/
@Component
@Slf4j
public class UploadApiLogInterceptor implements HandlerInterceptor {
@Resource
private NotificationService notificationService;
private final ObjectMapper mapper = new ObjectMapper();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ip = ServletUtil.getClientIP(request);
String client = "";
String tokenSha = "";
String id = "";
String name = "";
String school = "";
String scope = "";
String referer = request.getHeader("Referer");
Long requestTime = Instant.now().toEpochMilli();
// 获取 json 请求参数(这里读了后,后面就再也读不到了,需要处理)
BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = streamReader.readLine()) != null)
responseStrBuilder.append(inputStr);
JsonNode jsonNode = mapper.readTree(responseStrBuilder.toString());
String xAuthToken = request.getHeader("x-auth-AuthToken");
// 如果有 authorization
String authorization = request.getHeader("Authorization");
if (StringUtils.isNotBlank(authorization)) {
authorization = authorization.replace("Bearer ", "");
List<String> roles = (List<String>) JWTUtil.parseToken(authorization).getPayload("roles");
if (ObjectUtils.isNotEmpty(roles)) {
client = roles.get(0);
}
tokenSha = SecureUtil.sha1("Bearer " + authorization);
}
String xAuthIdToken = request.getHeader("X-Auth-IdToken");
if (StringUtils.isNotBlank(xAuthIdToken)) {
id = (String) JWTUtil.parseToken(xAuthIdToken).getPayload("sub");
name = (String) JWTUtil.parseToken(xAuthIdToken).getPayload("name");
if (StringUtils.isNotBlank(tokenSha)) {
tokenSha = SecureUtil.sha1(xAuthIdToken);
}
}
String xAuthSchool = request.getHeader("X-Auth-School");
if (StringUtils.isNotBlank(xAuthSchool)) {
school = xAuthSchool;
}
if (StringUtils.isNotBlank(xAuthToken)) {
id = (String) JWTUtil.parseToken(xAuthToken).getPayload("sub");
name = (String) JWTUtil.parseToken(xAuthToken).getPayload("name");
school = (String) JWTUtil.parseToken(xAuthToken).getPayload("azp");
scope = (String) JWTUtil.parseToken(xAuthToken).getPayload("scope");
if (StringUtils.isNotBlank(tokenSha)) {
tokenSha = SecureUtil.sha1(xAuthToken);
}
}
ObjectNode root = mapper.createObjectNode();
root.put("ip", ip);
root.put("time", requestTime);
root.put("id", id);
root.put("name", name);
root.set("param", jsonNode);
root.put("school", school);
root.put("client", client);
root.put("tid", tokenSha);
root.put("scope", scope);
root.put("path", request.getContextPath() + request.getRequestURI());
root.put("host", request.getServerName());
root.put("p", "appraisal");
// 发送请求
OkHttpClient okHttpClient = new OkHttpClient();
String requestData = root.toString();
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), requestData);
Request okRequest = new Request.Builder()
.url("https://teammodelosfunction.chinacloudsites.cn/api/http-log")
.post(requestBody)
.build();
okHttpClient.newCall(okRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
log.error("UploadApiLogIntercepter error" ) ;
notificationService.send("日志上传告警: 请求发送失败,检查请求发送客户端");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
log.error("UploadApiLogIntercepter error" ) ;
notificationService.send("日志上传告警: 请求响应异常,检查 API 源是否正常");
} else {
log.info("UploadApiLogIntercepter success");
}
}
});
return true;
}
}

@ -1,5 +1,6 @@
package cn.teammodel;
import cn.hutool.jwt.JWTUtil;
import cn.teammodel.common.FiveEducations;
import cn.teammodel.model.entity.appraise.Appraise;
import cn.teammodel.model.entity.appraise.AppraiseTreeNode;
@ -31,18 +32,9 @@ import java.util.UUID;
public class TestWithoutSpring {
@Test
public void testJwtDecode() {
// Claims claims = null;
// try {
// Jwts.parser()
//
//
//// claims = Jwts.parser()
//// .setSigningKey("fXO6ko/qyXeYrkecPeKdgXnuLXf9vMEtnBC9OB3s+aA=")
//// .parseClaimsJws("eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0LnRlYW1tb2RlbC5jbiIsInN1YiI6IjE1OTUzMjEzNTQiLCJhenAiOiJoYmNuIiwiZXhwIjoxNjk5NTg2MzM1LCJuYW1lIjoi572X6ICB5biIIiwicGljdHVyZSI6Imh0dHBzOi8vY29yZXN0b3JhZ2VzZXJ2aWNlLmJsb2IuY29yZS5jaGluYWNsb3VkYXBpLmNuL2FjY291bnQvYXZhdGFyLzE1OTUzMjEzNTQiLCJyb2xlcyI6WyJ0ZWFjaGVyIiwiYWRtaW4iLCJhcmVhIl0sInBlcm1pc3Npb25zIjpbXSwic3RhbmRhcmQiOiJzdGFuZGFyZDEiLCJzY29wZSI6InRlYWNoZXIiLCJhcmVhIjoiNjllM2Q0MTMtNTBhMS00ZjVlLTg0NGEtZTBmN2M5NjIyZWEzIiwid2Vic2l0ZSI6IklFUyJ9.4NdqwDdDQcyberEAirX0srOIb1ADXLJfP-a9qNXb0yw")
//// .getBody();
// } catch (Exception e) {
// e.printStackTrace();
// }
// 解析 JWT不验证签
Object roles = JWTUtil.parseToken("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlR4OFo1d2lkT2VSdDVCSGdWX2lvdXpzbjd6OCJ9.eyJhdWQiOiI3MjY0MzcwNC1iMmU3LTRiMjYtYjg4MS1iZDU4NjVlN2E3YTUiLCJpc3MiOiJodHRwczovL2xvZ2luLnBhcnRuZXIubWljcm9zb2Z0b25saW5lLmNuLzQ4MDdlOWNmLTg3YjgtNDE3NC1hYTViLWU3NjQ5N2Q3MzkyYi92Mi4wIiwiaWF0IjoxNzEwNzQ0MzA4LCJuYmYiOjE3MTA3NDQzMDgsImV4cCI6MTcxMDc0ODIwOCwiYWlvIjoiNDJaZ1lPQS9KL0t0ZTIzdWFzN2dEMGFPdW80WHpmdjlmMXhUREp6ZlZsNzk0VWkwWndVQSIsImF6cCI6ImM3MzE3Zjg4LTdjZWEtNGU0OC1hYzU3LWExNjA3MWY3Yjg4NCIsImF6cGFjciI6IjEiLCJvaWQiOiJkNDliYjc4Mi02ZGMyLTQ5MzktODY3OC1kMzNjMjY3MTliZjkiLCJyaCI6IjAuREFJQXota0hTTGlIZEVHcVctZGtsOWM1S3dRM1pITG5zaVpMdUlHOVdHWG5wNlVCQUFBLiIsInJvbGVzIjpbIklFUyJdLCJzdWIiOiJkNDliYjc4Mi02ZGMyLTQ5MzktODY3OC1kMzNjMjY3MTliZjkiLCJ0aWQiOiI0ODA3ZTljZi04N2I4LTQxNzQtYWE1Yi1lNzY0OTdkNzM5MmIiLCJ1dGkiOiJ6MGhKdDg0c1UwMlpxNTJNdGNRS0FBIiwidmVyIjoiMi4wIn0.U9KWPccJQ4ZgfzHBNTK6swuEJvkZM8ebfrOpAl7uDoNynlVEsp8CcQ2a1PIO3V8X8tcNassCplc08qbCqcL49R3S5vP2bWTiucT7c_fyCogxMYpmRM0RmGz8RF1tFF3VAxG4_q1Gt9LgzLbNhxxCQcbgJuZhUMKAP246esHlJrkue88FwgmMxf_rwxDH8VwbAchHtSuIRbj3zYSzgKU0Lpmibrr88g_gQZ061f4RyRezTmKLoOqhoGjEosOOnXOdI67YyxpdMjm64uzc92fNUyC9PHvu8XbTTbdUlkU2lPgrk5xFfyVus9SdxWQXB4PCIDhcBsoVQQlWGmY-kvw_8w").getPayload("roles");
System.out.println(roles);
}
@Test

Loading…
Cancel
Save