package com.hz.pm.api.user.security.config;

import com.hz.pm.api.common.model.constant.CommonConst;
import com.hz.pm.api.user.security.auth.code.AuthCodeLoginSecurityConfig;
import com.hz.pm.api.user.security.auth.credential.CredentialAuthSecurityConfig;
import com.hz.pm.api.user.security.handler.DefaultExpiredSessionStrategy;
import com.hz.pm.api.user.security.handler.DefaultLogoutSuccessHandler;
import com.hz.pm.api.user.security.auth.mh.MhAuthSecurityConfig;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

import java.util.Map;
import java.util.Set;

/**
 * <p>
 * WebSecurityConfig
 * </p>
 *
 * @author WendyYang
 * @since 13:24 2023/12/28
 */
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final AuthProperties authProperties;
    private final CredentialAuthSecurityConfig credentialAuthSecurityConfig;
    private final DefaultLogoutSuccessHandler logoutSuccessHandler;
    private final DefaultExpiredSessionStrategy defaultExpiredSessionStrategy;
    private final AuthCodeLoginSecurityConfig authCodeLoginSecurityConfig;
    private final MhAuthSecurityConfig mhAuthSecurityConfig;

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

    private void assemblerPreAuthUrls(HttpSecurity http) throws Exception {
        Map<String, String[]> roleArrayMap = authProperties.getRoleArrayMap();
        Set<String> roleSet = roleArrayMap.keySet();
        for (String role : roleSet) {
            String[] urlsArray = roleArrayMap.get(role);
            if (urlsArray != null && urlsArray.length > 0) {
                http.authorizeRequests().antMatchers(urlsArray).hasAuthority(role);
            }
        }
    }
}
