
package com.pavis.admin.config.satoken;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.filter.SaTokenContextFilterForJakartaServlet;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.sign.SaSignTemplate;
import cn.dev33.satoken.sign.SaSignUtil;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.DispatcherType;
import lombok.RequiredArgsConstructor;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.pavis.admin.common.context.UserContext;
import com.pavis.admin.common.context.UserContextHolder;
import com.pavis.admin.open.sign.OpenApiSignTemplate;
import org.springframework.core.Ordered;
import top.continew.starter.auth.satoken.autoconfigure.SaTokenExtensionProperties;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.exception.BusinessException;
import top.continew.starter.core.validation.CheckUtils;

import java.util.Collection;
import java.util.EnumSet;

/**
 * Sa-Token 配置
 *
 * @since 2022/12/19 22:13
 */
@Configuration
@RequiredArgsConstructor
public class SaTokenConfiguration {

    private final SaTokenExtensionProperties properties;
    private final LoginPasswordProperties loginPasswordProperties;
    private final OpenApiSignTemplate signTemplate;

    /**
     * Sa-Token 权限认证配置
     */
    @Bean
    public StpInterface stpInterface() {
        return new SaTokenPermissionImpl();
    }

    /**
     * SaToken 拦截器配置
     */
    @Bean
    public SaInterceptor saInterceptor() {
        SaManager.setSaSignTemplate(signTemplate);
        return new SaExtensionInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN)
                .notMatch(properties.getSecurity().getExcludes())
                .check(r -> {
                    // 如果包含 sign，进行 API 接口参数签名验证
                    SaRequest saRequest = SaHolder.getRequest();
                    Collection<String> paramNames = saRequest.getParamNames();
                    if (paramNames.stream().anyMatch(SaSignTemplate.sign::equals)) {
                        try {
                            SaSignUtil.checkRequest(saRequest);
                        } catch (Exception e) {
                            throw new BusinessException(e.getMessage());
                        }
                        return;
                    }
                    // 不包含 sign 参数，进行普通登录验证
                    StpUtil.checkLogin();
                    if (SaRouter.isMatchCurrURI(loginPasswordProperties.getExcludes())) {
                        return;
                    }
                    UserContext userContext = UserContextHolder.getContext();
                    CheckUtils.throwIf(userContext.isPasswordExpired(), "密码已过期，请修改密码");
                }));
    }

    @Bean
    public FilterRegistrationBean saTokenContextFilterForJakartaServlet() {
        FilterRegistrationBean bean = new FilterRegistrationBean<>(new SaTokenContextFilterForJakartaServlet());
// 配置 Filter 拦截的 URL 模式
        bean.addUrlPatterns("/*");
// 设置 Filter 的执行顺序,数值越小越先执行
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        bean.setAsyncSupported(true);
        bean.setDispatcherTypes(EnumSet.of(DispatcherType.ASYNC, DispatcherType.REQUEST));
        return bean;
    }
}
