package com.ningdatech.kqapi.security;

import com.ningdatech.basic.util.NdJsonUtil;
import com.ningdatech.basic.util.StrPool;
import com.ningdatech.kqapi.common.constant.BizConst;
import com.ningdatech.kqapi.security.handler.DefaultExpiredSessionStrategy;
import com.ningdatech.kqapi.security.handler.DefaultInvalidSessionStrategy;
import com.ningdatech.kqapi.security.password.PasswordAuthSecurityConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

import java.io.PrintWriter;

/**
 * <p>
 * WebSecurityConfig
 * </p>
 *
 * @author WendyYang
 * @since 11:07 2024/4/20
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final AuthProperties authProperties;
    private final PasswordAuthSecurityConfig passwordAuthSecurityConfig;
    private final LogoutSuccessHandler logoutSuccessHandler;
    private final DefaultExpiredSessionStrategy expiredSessionStrategy;
    private final DefaultInvalidSessionStrategy invalidSessionStrategy;

    public WebSecurityConfig(AuthProperties authProperties,
                             PasswordAuthSecurityConfig passwordAuthSecurityConfig,
                             LogoutSuccessHandler logoutSuccessHandler,
                             DefaultExpiredSessionStrategy expiredSessionStrategy,
                             DefaultInvalidSessionStrategy invalidSessionStrategy) {
        this.authProperties = authProperties;
        this.passwordAuthSecurityConfig = passwordAuthSecurityConfig;
        this.logoutSuccessHandler = logoutSuccessHandler;
        this.expiredSessionStrategy = expiredSessionStrategy;
        this.invalidSessionStrategy = invalidSessionStrategy;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint())
                .and().apply(passwordAuthSecurityConfig)
                .and()
                .authorizeRequests()
                .antMatchers(authProperties.getIgnoreAuthUrlsArray()).permitAll()
                .anyRequest()
                .authenticated()
                .and()
                // 防止固定会话攻击，Spring security的默认配置就是如此：
                // 登陆成功之后会创建一个新的会话，然后将旧的session信息复制到新的session中（客户端的sessionId变了）
                .sessionManagement()
                .invalidSessionStrategy(invalidSessionStrategy)
                .sessionFixation()
                .migrateSession()
                // .invalidSessionStrategy(defaultInvalidSessionStrategy)
                .maximumSessions(3)
                .maxSessionsPreventsLogin(true)
                .expiredSessionStrategy(expiredSessionStrategy)
                .and().and().logout().logoutUrl(authProperties.getLogoutUrl())
                .logoutSuccessHandler(logoutSuccessHandler)
                .deleteCookies(BizConst.COOKIE_KEY)
                .and()
                // 开启csrf验证，需要前端同步传入token
                .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .ignoringAntMatchers(authProperties.getIgnoreCsrfUrlsArray());
    }

    private AuthenticationEntryPoint authenticationEntryPoint() {
        return (request, response, authException) -> {
            response.setContentType(StrPool.CONTENT_TYPE);
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            PrintWriter writer = response.getWriter();
            writer.write(NdJsonUtil.getInstance().writeValueAsString(BizConst.UNAUTHENTICATED));
            writer.flush();
            writer.close();
        };
    }

}
