package com.ai.da.common.security.config;

import com.ai.da.common.security.*;
import com.ai.da.common.security.filter.AuthenticationFilter;
import com.ai.da.common.security.filter.UserAuthenticationProcessingFilter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.annotation.Resource;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private SecurityProperties securityProperties;
    @Resource
    private UserLoginSuccessHandler userLoginSuccessHandler;
    @Resource
    private UserLoginFailureHandler userLoginFailureHandler;
    @Resource
    private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
    @Resource
    private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
    @Resource
    private UserAuthenticationManager userAuthenticationManager;
    @Resource
    private UserAuthenticationProcessingFilter userAuthenticationProcessingFilter;

    /**
     * 不通过注入spring管理 让Security来管理 这样自定义的Filter就不会走,.permitAll()才能起作用
     */
    @Resource
    private AuthenticationFilter authenticationFilter;
    @Resource
    private UserPermissionEvaluator userPermissionEvaluator;


    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return this.userAuthenticationManager;
    }

    @Override
    protected void configure(HttpSecurity httpSecurity/*, WebSecurity web*/) throws Exception {
//        web.ignoring().antMatchers("/test/**");//禁止所有过滤器
        httpSecurity.cors().disable()//禁用 CSRF
                .authorizeRequests()//认证请求
                .antMatchers(securityProperties.getIgnorePaths()).permitAll()//忽略的请求
                .anyRequest().authenticated()//其余所有的请求都需要认证
                .and().headers().frameOptions().disable()// 防止iframe 造成跨域
                .and().exceptionHandling().authenticationEntryPoint(userAuthenticationEntryPointHandler)//未登录请求处理
                .accessDeniedHandler(userAuthAccessDeniedHandler)//无权限访问处理类 (此配置可以忽略，全局异常会先于Security框架处理异常，全局异常已特殊处理)
                .and().formLogin().loginProcessingUrl(securityProperties.getAuthApi())//指定认证接口
                .successHandler(userLoginSuccessHandler)//登录成功处理器
                .failureHandler(userLoginFailureHandler)//登录失败处理器
                .and().cors().and().csrf().disable();//允许跨域
        //自定义过滤器在登录时认证用户名、密码
        httpSecurity.addFilterAt(userAuthenticationProcessingFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(authenticationFilter, BasicAuthenticationFilter.class);
        //不创建session会话
        httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //取消头缓存控制
        httpSecurity.headers().cacheControl();
    }

    @Bean
    public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler() {
        DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
        handler.setPermissionEvaluator(userPermissionEvaluator);
        return handler;
    }
}
