package com.ningdatech.pmapi.user.security.auth;

import com.ningdatech.basic.util.NdJsonUtil;
import com.ningdatech.basic.util.StrPool;
import com.ningdatech.pmapi.common.constant.BizConst;
import com.ningdatech.pmapi.common.constant.CommonConst;
import com.ningdatech.pmapi.user.security.auth.handler.DefaultExpiredSessionStrategy;
import com.ningdatech.pmapi.user.security.auth.credential.CredentialAuthSecurityConfig;
import org.springframework.beans.factory.annotation.Qualifier;
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;
import java.util.Map;
import java.util.Set;

/**
 * @Author LiuXinXin
 * @Date 2020/7/28 4:14 下午
 * @Version 1.0
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final AuthProperties authProperties;
    private final CredentialAuthSecurityConfig credentialAuthSecurityConfig;
    private final LogoutSuccessHandler logoutSuccessHandler;
    private final DefaultExpiredSessionStrategy defaultExpiredSessionStrategy;

    public WebSecurityConfig(AuthProperties authProperties,
                             CredentialAuthSecurityConfig credentialAuthSecurityConfig,
                             @Qualifier(value = "defaultLogoutSuccessHandler") LogoutSuccessHandler logoutSuccessHandler,
                             DefaultExpiredSessionStrategy defaultExpiredSessionStrategy) {
        this.authProperties = authProperties;
        this.credentialAuthSecurityConfig = credentialAuthSecurityConfig;
        this.logoutSuccessHandler = logoutSuccessHandler;
        this.defaultExpiredSessionStrategy = defaultExpiredSessionStrategy;
    }

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

    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();
        };
    }

    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);
            }
        }
    }
}
