package com.steerinfo.auth.shiro.config;

import com.steerinfo.auth.shiro.cache.CustomCacheManager;
import com.steerinfo.auth.shiro.jwt.JwtStatelessAccessControlFilter;
import com.steerinfo.auth.shiro.realm.UserModularRealmAuthenticator;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.pam.AllSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Shiro权限配置
 * @ClassName: ShiroConfig 
 * @Description
 * @author 鄢利
 * @date 2018年7月19日 下午5:27:20 
 *
 */
@Configuration
public class ShiroConfig {

    private static final String AUTH = "authc";
    
    private static final String AUTH_ANON = "anon";

    @Value("${shiro.chains}")
    String chains;

    @Value("${shiro.chains.append}")
    String chainsAppend;

    @Value("${api.version}")
    private String apiVersion;

	/**
	* 
	* @param securityManager
	* @return
	*/
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/auth/login");
        // 定义shiro过滤器,例如实现自定义的FormAuthenticationFilter，需要继承FormAuthenticationFilter
        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put("auth", new JwtStatelessAccessControlFilter());
        shiroFilterFactoryBean.setFilters(filterMap);

        // 定义shiro过滤链 Map结构 * Map中key(xml中是指value值)的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的
        // anon：它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种
        // authc：该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        setFreezeAnonUrl(filterChainDefinitionMap);

        // <!-- 过滤链定义，从上向下顺序执行，一般将 /**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        // anon 可以理解为不拦截
        if (chains != null) {
            for (String chain : StringUtils.split(chains, ";")) {
                String[] str = StringUtils.split(chain, ",");
                filterChainDefinitionMap.put(str[0].trim(), str.length == 1 || str[1] == null || str[1].trim().equals("anon") ? AUTH_ANON : str[1].trim());
            }
            // 附加
            for (String chainAppend : StringUtils.split(chainsAppend, ";")){
                String[] str = StringUtils.split(chainAppend, ",");
                filterChainDefinitionMap.put(str[0].trim(), str.length == 1 || str[1] == null || str[1].trim().equals("anon") ? AUTH_ANON : str[1].trim());
            }
            //将 /**放在最为下边
            if (filterChainDefinitionMap.containsKey("/**")) {
                String allAuth = filterChainDefinitionMap.get("/**");
                filterChainDefinitionMap.remove("/**");
                filterChainDefinitionMap.put("/**", allAuth);
            }else{
            	filterChainDefinitionMap.put("/**", AUTH_ANON);
            }
        } else {
           filterChainDefinitionMap.put("/**", AUTH_ANON);
        }
        // TODO 所有URL都不认证
        // filterChainDefinitionMap.put("/**", AUTH_ANON);

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public JwtStatelessAccessControlFilter accessControlFilter(){
        JwtStatelessAccessControlFilter accessControlFilter = new JwtStatelessAccessControlFilter();
        return accessControlFilter;
    }

    @Bean
    public CacheManager customCacheManager() {
        CustomCacheManager cm = new CustomCacheManager();
        return cm;
    }

    /**
     * 安全管理器；即所有与安全有关的操作都会与SecurityManager交互；且它管理着所有Subject；
     * 可以看出它是Shiro的核心，它负责与后边介绍的其他组件进行交互，如果学习过SpringMVC，你可以把它看成DispatcherServlet前端控制器；
     * 不指定名字的话，自动创建一个方法名第一个字母小写的bean * @Bean(name = "securityManager")
     * @return
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("shiroRealms") List<Realm> shiroRealms) {
        DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
        dwsm.setRealms(shiroRealms);
        // 用户授权/认证信息Cache, 采用自定义缓存
        dwsm.setCacheManager(customCacheManager());
        //注入记住我管理器;
//        securityManager.setRememberMeManager(null);
        return dwsm;
    }

    /**
     * 系统自带的Realm管理，主要针对多realm
     * */
    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator(){
        //自己重写的ModularRealmAuthenticator
        UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
        return modularRealmAuthenticator;
    }
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager);
        return aasa;
    }

    /**
     * 设置拦截级别
     * 
     * @param filterChainDefinitionMap
     */
    private void setFreezeAnonUrl(Map<String, String> filterChainDefinitionMap){
    	String versionBefore = "/" + apiVersion;
        filterChainDefinitionMap.put("/", AUTH_ANON);
        filterChainDefinitionMap.put("/auth/login", AUTH_ANON); // 登录
        filterChainDefinitionMap.put("/static/**", AUTH_ANON); // 静态文件
        // 访问 /unauthorized/** 不通过JWTFilter
        filterChainDefinitionMap.put("/swagger-resources/**", AUTH_ANON);
        filterChainDefinitionMap.put("/swagger**", AUTH_ANON);
        filterChainDefinitionMap.put("/unauthorized/**", AUTH_ANON);
        // 获取日期
        filterChainDefinitionMap.put(versionBefore + "/common/**", AUTH_ANON);
        // mes接口
        filterChainDefinitionMap.put(versionBefore + "/ifemsmesactitems/**", AUTH_ANON);
        // 重新生成数据
        filterChainDefinitionMap.put(versionBefore + "/trmactvalues/**", AUTH_ANON);
        // 测试环境同步数据
        filterChainDefinitionMap.put(versionBefore + "/ifmesemsswapfile/**", AUTH_ANON);
        filterChainDefinitionMap.put(versionBefore + "/ifmesemsproductorder/**", AUTH_ANON);
        filterChainDefinitionMap.put(versionBefore + "/mesemsdieseloils/**", AUTH_ANON);
        // websocket不拦截
        filterChainDefinitionMap.put("/websocket/**", AUTH_ANON);
    }
    
}