내 연락처 정보
우편메소피아@프로톤메일.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
본 글에서는 springboot의 필터 클래스를 통해 ID 정보를 저장하기 위해 필터에 jwt 정보를 설정하는 방법을 주로 설명한다.
프로세스: 요청->필터->요청 본문 정보 구문 분석->threadlocal에 넣습니다.
필터 정의: /api 경로와 일치하는 모든 HTTP 요청을 차단하기 위해 @WebFilter 주석을 통해 등록되는 서블릿 사양을 사용하는 필터(Filter)입니다.
@WebFilter("/api") 주석은 필터가 /api 경로에 액세스하는 모든 요청에 적용되도록 지정합니다.
@Component 주석:
@Component는 JwtFilter가 Spring 컨테이너에서 관리할 수 있고 종속성 주입을 지원하는 Spring 구성 요소임을 나타내는 Spring 프레임워크의 주석입니다.
doFilter 메소드:
doFilter 메소드는 필터가 서블릿 또는 서블릿 컨테이너에 들어오는 요청과 응답을 가로채고 처리하는 방법을 정의합니다.
메소드 서명:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
이 메소드는 ServletRequest 요청, ServletResponse 응답 및 FilterChain 체인의 세 가지 매개변수를 허용합니다.
IOException 또는 ServletException이 발생할 수 있습니다.
요청 및 응답:
doFilter 메소드의 처음 두 매개변수는 현재 요청 및 응답 객체를 나타냅니다. 이 메소드에서 요청 데이터를 읽고 요청 및 응답을 수정할 수 있습니다.
일반적으로 doFilter 메서드 끝에서 chain.doFilter(request, response)를 호출하여 필터 체인의 다음 필터나 대상 리소스를 계속 실행해야 합니다.
요청 내용을 다시 수정하려면 HttpServletRequestWrapper를 사용할 수 있습니다. HttpServletRequestWrapper는 HttpServletRequest 인터페이스를 확장하는 래퍼 클래스로, 요청 처리를 수정하거나 확장할 수 있습니다. HttpServletRequestUriWrapper(HttpServletRequestWrapper에서 상속되는 사용자 정의 래퍼 클래스일 수 있음)를 사용하는 목적은 일반적으로 다음과 같습니다.
요청 URI 수정:
요청 URI는 수정하고 싶지만 원래 HttpServletRequest 객체는 수정할 수 없습니다. HttpServletRequestUriWrapper를 사용하면 원래 요청을 래핑하고 수정된 URI를 제공할 수 있습니다.
원래 요청을 변경하지 않고 유지합니다.
래퍼를 사용하면 원본 요청 개체를 변경하지 않고 유지하면서 필터링 체인의 특정 지점에서 요청의 특정 측면을 수정할 수 있습니다.
필터링 및 전처리:
filterChain.doFilter를 호출하기 전에 요청 매개변수 수정, 요청 경로 변경, 요청 헤더 추가 또는 수정 등과 같은 전처리 로직을 doFilter 메소드에 추가할 수 있습니다.
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
@WebFilter("/api")
@Component
@Slf4j
public class JwtFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
// noting to do
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
var httpRequest = (HttpServletRequest) servletRequest;
var requestBodyPayload = StreamUtils.copyToString(servletRequest.getInputStream(), StandardCharsets.UTF_8);
// 解析Body参数,并存入threadLocal管理
var jwtInfo = JwtUtil.getJwtInfoFromReq(requestBodyPayload);
JwtUtil.setJwtInfo(jwtInfo);
// 读取过body,需要重新设置body
var wrapper = new HttpServletRequestUriWrapper(httpRequest, httpRequest.getRequestURI(), requestBodyPayload);
// 将请求传递到下一个过滤器(或者最终到达控制器方法)
filterChain.doFilter(wrapper, servletResponse);
}
@Override
public void destroy() {
JwtUtil.removeJwtInfo();
MDC.clear();
}
}
JWT 정보:
@Slf4j
@Component
public class JwtUtil {
/** 线程jwt信息维护 */
private static final ThreadLocal<JwtInfo> REQUEST_BASE_INFO_THREAD_LOCAL = new ThreadLocal<>();
/** 解析jwt信息 */
public static JwtInfo getJwtInfoFromReq(String requestBodyPayload) {
var jwtInfo = new JwtInfo();
try {
var requestBody = JsonUtil.getJsonNode(requestBodyPayload);
log.info("[JwtUtil] RequestBody -> {}", requestBody);
// 解析requestBody,转为jwtInfo对象
jwtInfo.setRequestId(requestBody.get("RequestId") != null ? requestBody.get("RequestId").asText() : "");
jwtInfo.setRegion(requestBody.get("Region") != null ? requestBody.get("Region").asText() : "");
log.info("[JwtUtil] JwtInfo -> {}", jwtInfo);
} catch (Exception e) {
log.error("[JwtUtil] Parse RequestBodyInfo Error, Error Message -> {}", e.getMessage(), e);
}
return jwtInfo;
}
/** 获取jwt信息 */
public static JwtInfo getJwtInfo() {
var jwtInfo = REQUEST_BASE_INFO_THREAD_LOCAL.get();
if (Objects.isNull(jwtInfo)) {
final var requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (Objects.nonNull(requestAttributes)) {
var requestBodyPayload = "";
try {
requestBodyPayload = StreamUtils.copyToString(requestAttributes.getRequest().getInputStream(),
StandardCharsets.UTF_8);
} catch (Exception e) {
log.error("[JwtUtil] Parse RequestBodyInfo Error, Error Message -> {}", e.getMessage());
}
jwtInfo = getJwtInfoFromReq(requestBodyPayload);
setJwtInfo(jwtInfo);
}
}
return jwtInfo;
}
/** 将jwt信息存入threadLocal中 */
public static void setJwtInfo(JwtInfo jwtInfo) {
REQUEST_BASE_INFO_THREAD_LOCAL.set(jwtInfo);
// 将traceId写入日志变量
MDC.put("traceId", jwtInfo.getRequestId());
}
public static void setJwtInfo(String appId, String ownerUin) {
var jwtInfo = new JwtUtil.JwtInfo();
jwtInfo.setRequestId(UUID.randomUUID().toString());
setJwtInfo(jwtInfo);
}
/** 从threadLocal中删除jwt信息 */
public static void removeJwtInfo() {
REQUEST_BASE_INFO_THREAD_LOCAL.remove();
}
@Data
public static class JwtInfo {
@JsonPropertyDescription("请求requestId")
private String requestId;
@JsonPropertyDescription("请求的Region")
private String region;
}
}
jwt에서 콘텐츠를 가져오고 다른 http 요청을 보냅니다.
public static JsonNode sendHttpRequest(String method, String action, String url, Map<String, Object> body)
throws IOException, InterruptedException {
// 设置通用参数
var jwtInfo = JwtUtil.getJwtInfo();
if (jwtInfo != null) {
body.put("RequestId", jwtInfo.getRequestId());
body.put("AppId", Integer.valueOf(jwtInfo.getAppId()));
body.put("Uin", jwtInfo.getUin());
body.put("Region", jwtInfo.getRegion());
}
// 设置action
body.put("Action", action);
// 发送http请求,拿到请求结果
HttpConnectUtil.ResponseInfo responseInfo = switch (method) {
case "GET" -> HttpConnectUtil.sendGetByJson(url, JsonUtil.toJson(body));
case "POST" -> HttpConnectUtil.sendPost(url, JsonUtil.toJson(body), new HashMap<>(2));
default -> new HttpConnectUtil.ResponseInfo();
};
// 检查Api3格式返回结果,并解析
var jsonResponse = JsonUtil.getJsonNode(responseInfo.getContent()).get("Response");
var jsonError = jsonResponse.get("Error");
if (jsonError != null) {
var errorCode = jsonError.get("Code").asText();
var errorMessage = jsonError.get("Message").asText();
throw new ApiException(ErrorCode.INTERNAL_ERROR,
String.format("错误码:[%s],错误信息:[%s]", errorCode, errorMessage));
}
return jsonResponse;
}