package com.project.shiro.config;

import com.project.shiro.util.AuthenticationFilter;
import com.project.shiro.util.AuthenticationRealm;
import com.project.shiro.util.AuthorizationFilter;
import com.project.shiro.util.redis.ShiroRedisCacheManager;
import com.project.shiro.util.redis.ShiroRedisSessionDAO;
import org.apache.shiro.session.mgt.SessionManager;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Description: shiro配置类
 * User: mxy
 * Date: 2019-04-16
 */
@Configuration
public class ShiroConfig {

    private static final transient Logger logger = LoggerFactory.getLogger(ShiroConfig.class);

    /**
     * 配置拦截器
     * <p>
     * 定义拦截URL权限，优先级从上到下
     * 1). anon  : 匿名访问，无需登录
     * 2). authc : 登录后才能访问
     * 3). logout: 登出
     * 4). frameperms : 自定义的过滤器
     * <p>
     * URL 匹配风格
     * 1). ?：匹配一个字符，如 /admin? 将匹配 /admin1，但不匹配 /admin 或 /admin/；
     * 2). *：匹配零个或多个字符串，如 /admin* 将匹配 /admin 或/admin123，但不匹配 /admin/1；
     * 3). **：匹配路径中的零个或多个路径，如 /admin/** 将匹配 /admin/a 或 /admin/a/b
     * <p>
     * 配置身份验证成功，失败的跳转路径
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        logger.info("进入Shiro拦截工厂");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 自定义的过滤器
        Map<String, Filter> filterMap = new HashMap<>();
        // map里面key值要为过滤器的名称，value为过滤器对象
        filterMap.put("authc", authenticationFilter());
        filterMap.put("frameperms", authorizationFilter());
        // 将自定义的过滤器加入到过滤器集合中
        shiroFilterFactoryBean.setFilters(filterMap);

        // 设置拦截器集合
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        filterChainDefinitionMap.put("/admin/", "anon"); // 后台资源-匿名访问
        filterChainDefinitionMap.put("/admin/res/**", "anon"); // 静态资源-匿名访问
        filterChainDefinitionMap.put("/admin/anon/**", "anon"); // 后台可匿名访问资源-匿名访问
        filterChainDefinitionMap.put("/admin/login", "authc"); // 登录页面-身份认证
        filterChainDefinitionMap.put("/admin/logout", "logout");    // 用户退出，只需配置logout即可实现该功能
        filterChainDefinitionMap.put("/admin/common/**", "anon");  // 其他路径均需要身份认证，一般位于最下面，优先级最低
        filterChainDefinitionMap.put("/admin/**", "authc,frameperms");  // 其他路径均需要身份认证，一般位于最下面，优先级最低

        // 设置拦截器
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        shiroFilterFactoryBean.setLoginUrl("/admin/login");       // 登录的路径
//        shiroFilterFactoryBean.setUnauthorizedUrl("/admin/common/unauthorized.jhtml");  // 验证失败后跳转的路径
        logger.info("Shiro拦截工厂配置完成");
        return shiroFilterFactoryBean;
    }

    /**
     * 配置Shiro生命周期处理器
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 自动创建代理类，若不添加，Shiro的注解可能不会生效。
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启Shiro的注解
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 配置加密匹配，使用MD5的方式，进行1024次加密
     */
//    @Bean
//    public HashedCredentialsMatcher hashedCredentialsMatcher() {
//        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
//        hashedCredentialsMatcher.setHashIterations(1024);
//        return hashedCredentialsMatcher;
//    }

    /**
     * SecurityManager 安全管理器；Shiro的核心
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 自定义的Realm
        securityManager.setRealm(authenticationShiroRealm());
        // 缓存管理
        securityManager.setCacheManager(shiroRedisCacheManager());
        // 会话管理
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * 自定义Realm，可以多个
     */
    @Bean
    public AuthenticationRealm authenticationShiroRealm() {
        AuthenticationRealm authenticationRealm = new AuthenticationRealm();
        //authenticationRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return authenticationRealm;
    }

    /**
     * redis缓存管理
     */
    @Bean
    public ShiroRedisCacheManager shiroRedisCacheManager() {
        return new ShiroRedisCacheManager();
    }

    /**
     * 设置session会话管理者
     */
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        defaultWebSessionManager.setSessionIdCookie(simpleCookie());
        defaultWebSessionManager.setSessionDAO(shiroRedisSessionDAO());
        return defaultWebSessionManager;
    }

    /**
     * session管理
     */
    @Bean
    public ShiroRedisSessionDAO shiroRedisSessionDAO() {
        return new ShiroRedisSessionDAO();
    }

    /**
     * 这里需要设置一个cookie的名称  原因就是会跟原来的session的id值重复的
     */
    @Bean
    public SimpleCookie simpleCookie() {
        return new SimpleCookie("SHAREJSESSIONID");
    }


    @Bean
    public AuthenticationFilter authenticationFilter() {
        return new AuthenticationFilter();
    }

    @Bean
    public AuthorizationFilter authorizationFilter() {
        return new AuthorizationFilter();
    }

}
