package com.kxs.adminap.filter;

 
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import com.kxs.adminap.util.JwtUtil;

import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
 
/**
 * 如果请求中（请求头或者Cookie）中存在JWT，则：
 * 1、解析JWT并查找对应的用户信息，然后加入request attribute中
 * 2、更新Cookie时间、更新JWT失效时间放入Header
 * <p>
 * OncePerRequestFilter 一次请求只进入一次filter
 */
@Component
public class JWTFilter extends OncePerRequestFilter {
 
    public static final String SECURITY_USER = "SECURITY_USER";
 
    // 设置不需要校验的路径
    private static final String[] NOT_CHECK_URL = {"/login"};
 
    @Autowired
    private JwtUtil jwtUtil;
 
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
 
        // 判断是否需要对token处理
        if (!isNotCheck(request.getRequestURI())) {
            // 获取token
            String token = getToken(request);
 
            if (token.isBlank()) {
                request.setAttribute("exceptionCode", 10001);
                request.setAttribute("exceptionMessage", "请求无效，原因：" + jwtUtil.getHeader() + " 为空！");
                request.getRequestDispatcher("/exception/authentication").forward(request, response);
                // throw new AuthenticationException(SystemParameters.TOKEN_IS_NULL.getIndex(),  "请求无效，原因：" + jwtUtil.getHeader() + " 为空！");
                return;
            }
 
            Claims claims = jwtUtil.parseToken(token);
 
            // 判断签名信息
            if (null != claims && !claims.isEmpty() && !jwtUtil.isExpired(claims.getExpiration())) {
                // 获取签名用户信息
                String userId = claims.getSubject();
                //获取相应的用户信息，可以在过滤器中先行获取，也可以先保存用户ID，在需要时进行获取
                // User user = usersService.findById(Long.valueOf(userId));
 
                request.setAttribute(SECURITY_USER, userId);
                Cookie jwtCookie = new Cookie(jwtUtil.getHeader(), URLEncoder.encode(token, "UTF-8"));
                jwtCookie.setHttpOnly(true);
                jwtCookie.setPath("/");
                response.addCookie(jwtCookie);
 
                response.addHeader(jwtUtil.getHeader(), token);
            } else {
                request.setAttribute("exceptionCode", 10001);
                request.setAttribute("exceptionMessage", jwtUtil.getHeader() + " 无效，请重新登录！");
                request.getRequestDispatcher("/exception/authentication").forward(request, response);
                // throw new AuthenticationException(SystemParameters.AUTHENTICATION_FAILED.getIndex(), jwtUtil.getHeader() + " 无效，请重新登录！");
                return;
            }
        }
 
        filterChain.doFilter(request, response);
    }
 
    /**
     * 获取token
     *
     * @param request
     * @return 返回token
     */
    private String getToken(HttpServletRequest request) {
 
        //先从header中获取token
        String token = request.getHeader(jwtUtil.getHeader());
 
        //再从cookie中获取
        if (token.isBlank()) {
            try {
                Cookie[] cookies = request.getCookies();
                if (cookies != null) {
                    for (Cookie cookie : cookies) {
                        if (jwtUtil.getHeader().equals(cookie.getName())) {
                            token = URLDecoder.decode(cookie.getValue(), "UTF-8");
                        }
                    }
                }
            } catch (Exception e) {
            }
        }
 
        return token;
    }
 
    /**
     * 根据url判断是否需要校验，false需要校验
     *
     * @param url
     * @return 是否需要校验
     */
    private boolean isNotCheck(String url) {
 
        // 处理路径以"/" 结尾的"/"
        url = url.endsWith("/") ? url.substring(0, url.lastIndexOf("/")) : url;
 
        for (String path : NOT_CHECK_URL) {
            // 判断是否以 "/**" 结尾
            if (path.endsWith("/**")) {
                return url.startsWith(path.substring(0, path.lastIndexOf("/") + 1))
                        || url.equals(path.substring(0, path.lastIndexOf("/")));
            }
 
            // 判断url == path
            if (url.equals(path)) {
                return true;
            }
        }
 
        return false;
    }
 
}