2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Cet article décrit principalement la méthode de définition des informations jwt dans le filtre pour enregistrer les informations d'identité via la classe de filtre de Springboot.
Processus : Requête->Filtre->Analyser les informations du corps de la requête->Placez-les dans threadlocal
Définir un filtre : un filtre (Filter) utilisant la spécification Servlet, qui est enregistré via l'annotation @WebFilter pour intercepter toutes les requêtes HTTP correspondant au chemin /api.
L'annotation @WebFilter("/api") spécifie que le filtre sera appliqué à toutes les requêtes accédant au chemin /api.
@Annotation du composant :
@Component est une annotation du framework Spring, indiquant que JwtFilter est un composant Spring qui peut être géré par le conteneur Spring et prend en charge l'injection de dépendances.
Méthode doFilter :
La méthode doFilter définit la manière dont le filtre intercepte et traite les requêtes et les réponses entrant dans le servlet ou le conteneur de servlet.
Signature de la méthode :
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
Cette méthode accepte trois paramètres : requête ServletRequest, réponse ServletResponse et chaîne FilterChain.
Il peut lancer IOException ou ServletException.
Demande et réponse :
Les deux premiers paramètres de la méthode doFilter représentent les objets de requête et de réponse actuels. Vous pouvez lire les données de requête et modifier les requêtes et les réponses dans cette méthode.
En règle générale, à la fin de la méthode doFilter, vous devez appeler chain.doFilter(request, réponse) pour continuer à exécuter le filtre ou la ressource cible suivante dans la chaîne de filtres.
Si vous souhaitez re-modifier le contenu de la requête, vous pouvez utiliser HttpServletRequestWrapper. HttpServletRequestWrapper est une classe wrapper qui étend l'interface HttpServletRequest, vous permettant de modifier ou d'étendre le traitement des requêtes. Les objectifs de l'utilisation de HttpServletRequestUriWrapper (qui peut être une classe wrapper personnalisée héritant de HttpServletRequestWrapper) incluent généralement :
Modifier l'URI de la requête :
Vous souhaiterez peut-être modifier l'URI de la demande, mais pas l'objet HttpServletRequest d'origine. En utilisant HttpServletRequestUriWrapper, vous pouvez encapsuler la demande d'origine et fournir un URI modifié.
Conservez la demande d'origine inchangée :
L'utilisation d'un wrapper conserve l'objet de requête d'origine inchangé tout en vous permettant de modifier certains aspects de la requête à un moment donné de la chaîne de filtrage.
Filtrage et prétraitement :
Avant d'appeler filterChain.doFilter, vous pouvez ajouter n'importe quelle logique de prétraitement dans la méthode doFilter, telle que la modification des paramètres de requête, la modification des chemins de requête, l'ajout ou la modification d'en-têtes de requête, etc.
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();
}
}
informations 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;
}
}
Récupérez le contenu dans jwt et envoyez d'autres requêtes 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;
}