package com.mydreamjob.deletion; import com.fasterxml.jackson.databind.ObjectMapper; import com.mydreamjob.common.api.ApiResponse; import com.mydreamjob.common.exception.ErrorCode; import com.mydreamjob.security.CurrentUserAccessor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.http.MediaType; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import java.util.Set; /** * 注销守卫拦截器。 * *
基于当前已认证用户的账号注销状态拦截业务请求: *
身份解析复用唯一的 {@link CurrentUserAccessor}(R01-A):本拦截器注册在 * {@code /**},先于所有 Controller 执行,因此绝不能自己解析主体。此前它用 * {@code Character::isDigit} 判断后直接 {@code Long.valueOf(name)},导致 Long 溢出主体 * (如 {@code "99999999999999999999"})抛出 {@code NumberFormatException},在到达任何 * Controller 之前就逃逸为全站 HTTP 500。现在非法主体一律视为未认证并放行, * 由 Controller 统一返回 401。
*/ @Component @RequiredArgsConstructor public class DeletionGuardInterceptor implements HandlerInterceptor { private static final Set该安全依赖由构造器强制注入;装配缺失时应用必须失败启动。
*/ private Long resolveUserIdOrNull(Authentication authentication) { try { return currentUserAccessor.requireUserId(authentication); } catch (InsufficientAuthenticationException e) { return null; } } private boolean reject(HttpServletResponse response, ErrorCode errorCode, HttpServletRequest request) throws Exception { response.setStatus(409); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); String requestId = request.getHeader("X-Request-Id"); response.getWriter().write( objectMapper.writeValueAsString(ApiResponse.error(errorCode.getCode(), errorCode.getMessage(), requestId))); return false; } /** 解析去除 context-path 的请求路径,兼容真实服务器与 MockMvc。 */ private String resolvePath(HttpServletRequest request) { String uri = request.getRequestURI(); String ctx = request.getContextPath(); if (ctx != null && !ctx.isEmpty() && uri.startsWith(ctx)) { return uri.substring(ctx.length()); } return uri; } }