package cn.reghao.autodop.dmaster.rbac.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.web.FilterInvocation;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * Web 应用安全配置
 *
 * @author reghao
 * @date 2019-05-20 10:41:10
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 调用方法时检查权限
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    private final UserDetailsServiceImpl userDetailsService;

    public WebSecurityConfig(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .expressionHandler(webExpressionHandler())
                .antMatchers("/login").permitAll()
                // TODO 处理 WebSocket 的认证
                .antMatchers("/webssh").permitAll()
                // 放行所有以 /api 为前缀的请求
                .antMatchers("/api/**").permitAll()
                .anyRequest().authenticated();
        http.exceptionHandling().accessDeniedPage("/deny");

        // 禁用 CSRF
        http.csrf().disable();

        // 处理 X-Frame-Options header
        http.headers().frameOptions().sameOrigin();

        // 在 UsernamePasswordAuthenticationFilter 后添加 filter
        http.addFilterAfter(new LoginRedirectFilter(), UsernamePasswordAuthenticationFilter.class);

        // 基于表单的认证
        http.formLogin()
                // 登录页面(GET 请求)
                .loginPage("/login")
                // 登录接口(POST 请求)
                .loginProcessingUrl("/login")
                .successHandler(new WebAuthSuccessHandlerImpl())
                .failureHandler(new WebAuthFailureHandlerImpl())
                .and()
                .logout()
                .logoutSuccessUrl("/")
                .and()
                .httpBasic().disable();

        // HTTP Basic 认证
        http.httpBasic();
    }

    /**
     * 使用数据库存储
     *
     * @param
     * @return
     * @date 2019-07-05 上午11:25
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
        // TODO 尝试使用 DaoAuthenticationProvider
        auth.authenticationProvider(new UsernamePasswordAuthProvider(userDetailsService));
    }

    /**
     * 角色继承
     * ADMIN 可以访问 USER 的权限，反之不可
     *
     * @param
     * @return
     * @date 2019-07-05 上午11:18
     */
    @Bean
    public RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
        return roleHierarchy;
    }

    private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() {
        DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler =
                new DefaultWebSecurityExpressionHandler();
        defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy());

        return defaultWebSecurityExpressionHandler;
    }

    /**
     * 放行静态资源
     *
     * @param
     * @return
     * @date 2021-04-04 下午9:06
     */
    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers("/css/**")
                .antMatchers("/imgs/**")
                .antMatchers("/js/**")
                .antMatchers("/lib/**");
    }
}
