package cn.edu.whu.boot.user.config;

import cn.edu.whu.boot.user.components.CustomSessionListener;
import cn.edu.whu.boot.user.realms.UserRealm;
import cn.edu.whu.boot.common.utils.user.PasswordEncoder;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher()
    {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(PasswordEncoder.getALGORITHM());
        hashedCredentialsMatcher.setHashIterations(PasswordEncoder.getITERATION());
        return hashedCredentialsMatcher;
    }

    @Bean
    public UserRealm shiroRealm()
    {
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return userRealm;
    }

    // 使用 Spring Boot 的场景中，这个 Bean 的类型是 SessionsSecurityManager
    @Bean
    public SessionsSecurityManager securityManager()
    {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator()
    {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor()
    {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor()
    {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean()
    {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager());
        filterFactoryBean.setLoginUrl("/user/unauthc"); // 用户没有认证时请求此 URL
        filterFactoryBean.setUnauthorizedUrl("/user/unautho"); // 用户没有权限时请求此 URL
        // 配置 URL 的访问权限
        Map<String, String> definitionMap = new HashMap<String, String>() {{
            // 无需认证
            put("/user/password_recover/authentication", "anon");
            put("/user/password_recover/action", "anon");
            put("/user/password_reset", "anon");
            put("/static/**", "anon");
            //需要认证和特定权限的 URL
            //put("/user/all", "authc, perms[user:read]");
            //put("/user/all_worker", "authc, perms[user:readWorker]");
            //put("/user/role_change", "authc, perms[user:changeRole]");
            //put("/user/new", "authc, perms[user:new]");
            //put("/allocation/assignment", "authc, perms[task:assign]");
            //put("/allocation/info/all", "authc, perms[task:read]");
            //put("/allocation/info/worker", "authc, perms[task:readWorker]");
            //put("/allocation/change/cell", "authc, perms[task:changeCellToUser]");
            //put("/allocation/change/cell_worker", "authc, perms[task:changeCellToUser]");
            //put("/allocation/change/worker", "authc, perms[task:appendWorkersToTask]");
            //put("/allocation/change/data", "authc, perms[task:changeDataToUser]");
            //put("/allocation/worker/appending", "authc, perms[task:appendWorkersToTask]");
            //put("/allocation/worker/deleting", "authc, perms[task:deleteWorker]");
            //put("/allocation/worker_status", "authc, perms[task:checkWorkerStatus]");
            //put("/allocation/cell/worker/appending", "authc, perms[task:appendWorkersToCell]");
            //put("/allocation/cell/worker/deleting", "authc, perms[task:deleteWorkerFromCell]");
            //put("/base/rollBackDataForVersion", "authc");
            //put("/base/setDataNotPass", "authc");
            //put("/base/getWarningForUser", "authc");
            //put("/base/getCountByGeoHash", "authc");
            //put("/get/getAllData", "authc");
            //put("/get/getCountByFuzzySearchAndGeohash", "authc");
            //put("/get/getDataByFuzzySearchAndRangeIntersection", "authc");
            //put("/get/getDataByRangeIntersection", "authc");
            //put("/table/**", "authc");
            //put("/basicStat/**", "authc");
            //put("/statistic/**", "authc");
            //put("/report/**", "authc");
            //put("/task/selectAll", "authc");
            //put("/task/selectAllWithTableName", "authc");
            //put("/task/selectTaskHasCreated", "authc");
        }};
        filterFactoryBean.setFilterChainDefinitionMap(definitionMap);
        return filterFactoryBean;
    }

    /**
     * 配置 session 保存的方式
     */
    @Bean
    public SessionDAO sessionDAO()
    {
        return new MemorySessionDAO();
    }

    /**
     * 配置 session 管理
     *
     * @return SessionManager
     */
    @Bean
    public SessionManager sessionManager()
    {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        Collection<SessionListener> listeners = new ArrayList<>();
        //  配置 session 监听器
        listeners.add(sessionListener());
        sessionManager.setSessionListeners(listeners);
        sessionManager.setSessionDAO(sessionDAO());
        // 指定本系统 SESSIONID, 默认为: JSESSIONID 问题: 与 SERVLET 容器名冲突, 如JETTY, TOMCAT 等默认 JSESSIONID,
        // 当跳出 SHIRO SERVLET 时如 ERROR-PAGE 容器会为 JSESSIONID 重新分配值导致登录会话丢失!
        sessionManager.setSessionIdCookie(new SimpleCookie("shiro.session.id"));
        sessionManager.setGlobalSessionTimeout(2 * 60 * 60 * 1000); // 设置全局 session 过期时间为 2 小时
        sessionManager.setSessionValidationInterval(60 * 60 * 1000); // 每隔 1 小时清除用户关闭页面但是没有手动登出造成的孤立 session
        sessionManager.setSessionIdUrlRewritingEnabled(false); // 去掉 URL 后面的 JSESSIONID
        return sessionManager;
    }

    @Bean
    public SessionListener sessionListener()
    {
        return new CustomSessionListener();
    }
}