2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Tässä artikkelissa kuvataan pääasiassa menetelmää, jolla jwt-tiedot asetetaan suodattimeen identiteettitietojen tallentamiseksi springbootin suodatinluokan kautta.
Prosessi: Pyyntö-> Suodatin-> Jäsennä pyynnön runkotiedot-> Laita se säiettälokaaliseen
Määritä suodatin: Servlet-määrityksiä käyttävä suodatin (Filter), joka on rekisteröity @WebFilter-merkinnän kautta sieppaamaan kaikki /api-polkua vastaavat HTTP-pyynnöt.
@WebFilter("/api") -merkintä määrittää, että suodatinta sovelletaan kaikkiin pyyntöihin, jotka käyttävät /api-polkua.
@Komponenttimerkintä:
@Component on Spring-kehyksen huomautus, joka osoittaa, että JwtFilter on Spring-komponentti, jota Spring-säilö voi hallita ja joka tukee riippuvuuden lisäystä.
doFilter-menetelmä:
doFilter-menetelmä määrittää, kuinka suodatin sieppaa ja käsittelee pyynnöt ja vastaukset, jotka tulevat Servletiin tai Servlet-säilöön.
Menetelmän allekirjoitus:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
Tämä menetelmä hyväksyy kolme parametria: ServletRequest-pyyntö, ServletResponse-vastaus ja FilterChain-ketju.
Se voi lähettää IOExceptionin tai ServletExceptionin.
Pyyntö ja vastaus:
DoFilter-menetelmän kaksi ensimmäistä parametria edustavat nykyisiä pyyntö- ja vastausobjekteja. Voit lukea pyyntötietoja ja muokata pyyntöjä ja vastauksia tässä menetelmässä.
Yleensä doFilter-menetelmän lopussa sinun on kutsuttava ketju.doFilter(request, response), jotta voit jatkaa seuraavan suodattimen tai kohderesurssin suorittamista suodatinketjussa.
Jos haluat muokata pyynnön sisältöä, voit käyttää HttpServletRequestWrapperia. HttpServletRequestUriWrapperin (joka voi olla mukautettu kääreluokka, joka perii HttpServletRequestWrapper) käyttötarkoituksia ovat yleensä:
Muokkaa pyynnön URI:tä:
Haluat ehkä muokata pyynnön URI-osoitetta, mutta et alkuperäistä HttpServletRequest-objektia. Käyttämällä HttpServletRequestUriWrapperia voit kääriä alkuperäisen pyynnön ja antaa muokatun URI:n.
Pidä alkuperäinen pyyntö ennallaan:
Käärityksen käyttäminen pitää alkuperäisen pyyntöobjektin muuttumattomana samalla, kun voit muokata tiettyjä pyynnön näkökohtia jossain vaiheessa suodatusketjua.
Suodatus ja esikäsittely:
Ennen filterChain.doFilterin kutsumista voit lisätä doFilter-menetelmään mitä tahansa esikäsittelylogiikkaa, kuten muokata pyyntöparametreja, muuttaa pyyntöpolkuja, lisätä tai muokata pyyntöotsikoita jne.
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 tiedot:
@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;
}
}
Hanki sisältö jwt:ssä ja lähetä muita http-pyyntöjä:
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;
}