package com.xyw.console.auth;
import com.xyw.console.asset.entity.SystemUserEntity;
import com.xyw.console.asset.mapper.SystemUserMapper;
import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.util.Collections;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter;
@Component
public class AuthTokenFilter extends OncePerRequestFilter {
  private static final Logger log = LoggerFactory.getLogger(AuthTokenFilter.class);
  private final AuthTokenService tokens; private final SystemUserMapper users; private final PagePermissionService permissions;
  /** 代码作用（白话）：接收令牌解析、用户读取和权限计算依赖，用于把浏览器 Cookie 恢复成当前登录身份；关联文件：AuthTokenService.java、SystemUserMapper.java、PagePermissionService.java；关联逻辑（调用链/数据流）：SecurityConfig 安全链 -> 本过滤器 -> Cookie/JWT -> 用户记录 -> SecurityContext。 */
  public AuthTokenFilter(AuthTokenService tokens, SystemUserMapper users, PagePermissionService permissions) { this.tokens=tokens; this.users=users; this.permissions=permissions; }
  /** 代码作用（白话）：跳过登录和 CSRF 初始化接口，避免公开入口被会话恢复逻辑拦截；关联文件：AuthController.java、SecurityConfig.java；关联逻辑（调用链/数据流）：/api/auth/login 或 /api/auth/csrf -> Controller，其他接口 -> doFilterInternal()。 */
  @Override protected boolean shouldNotFilter(HttpServletRequest request) { return "/api/auth/login".equals(request.getRequestURI()) || "/api/auth/csrf".equals(request.getRequestURI()); }
  /** 代码作用（白话）：读取会话 Cookie，验证令牌和账号状态后建立当前请求的登录身份；失败时保留 401 边界并输出不含凭证的诊断类别；关联文件：AuthTokenService.java、SystemUserEntity.java、SecurityConfig.java；关联逻辑（调用链/数据流）：受保护请求 -> Cookie -> JWT -> 用户状态/版本 -> SecurityContext -> Controller 或统一 401。 */
  @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { try { String token=tokens.extract(request.getCookies()); if (token == null) log.info("AUTH_SESSION_MISSING method={} path={}", request.getMethod(), request.getRequestURI()); else { AuthPrincipal principal=tokens.parse(token); SystemUserEntity user=users.selectById(principal.userId()); if (user != null && "ACTIVE".equals(user.getStatus()) && user.getAuthVersion()!=null && user.getAuthVersion()==principal.authVersion()) { var authentication=new UsernamePasswordAuthenticationToken(principal, null, Collections.singleton(new SimpleGrantedAuthority("ROLE_"+principal.roleCode()))); authentication.setDetails(permissions.effectivePermissions(user)); SecurityContextHolder.getContext().setAuthentication(authentication); } else { SecurityContextHolder.clearContext(); log.info("AUTH_SESSION_REJECTED reason=USER_STATE_OR_VERSION method={} path={}", request.getMethod(), request.getRequestURI()); } } } catch (Exception error) { SecurityContextHolder.clearContext(); log.warn("AUTH_SESSION_ERROR type={} method={} path={}", error.getClass().getSimpleName(), request.getMethod(), request.getRequestURI()); } chain.doFilter(request,response); }
}
