//druid连接池配置   
@Configuration
public class DruidConfig {
    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        // 登录查看信息的账号密码.
        servletRegistrationBean.addInitParameter("loginUsername", "admin");
        servletRegistrationBean.addInitParameter("loginPassword", "123456");
        return servletRegistrationBean;
    }
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
}
@Configuration
@EnableCaching//开启换成配置
public class RedisConfig extends CachingConfigurerSupport {
    private static Logger Logger = LoggerFactory.getLogger(RedisConfig.class);
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.pool.max-wait}")
    private long maxWaitMillis;
    @Bean
    public JedisPool redisPoolFactory() {
        Logger.info("开启redis,redis地址:" + host + ":" + port + ",JedisPool注入成功!!");
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout);
        return jedisPool;
    }
    /**
     * 配置shiro redisManager 使用的是shiro-redis开源插件
     * 
     * @return
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setExpire(1800);// 配置缓存过期时间
        redisManager.setTimeout(timeout);
        return redisManager;
    }
}
@Configuration
public class ShiroConfig {
    /**
     * 缓存和session的管理
     */
    @Autowired
    private ShiroSessionAndCache shiroSessionAndCache;
    
    /**
     * shiro 服务
     */
    @Autowired
    private ShiroService shiroService;
    private static Logger Logger = LoggerFactory.getLogger(ShiroConfig.class);
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    /**
     * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
     * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager Filter Chain定义说明 
     * 1、一个URL可以配置多个Filter,使用逗号分隔
     * 2、当设置多个过滤器时,全部验证通过,才视为通过 
     * 3、部分过滤器可指定参数,如perms,roles
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(org.apache.shiro.mgt.SecurityManager securityManager) {
        Logger.info("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login"页面
        shiroFilterFactoryBean.setLoginUrl("/login"); //登录页面
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");//首页
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");//错误页面
        // 拦截器.  权限配置
        Map<String, String> filterChainDefinitionMap = shiroService.loadFilterChainDefinitions();
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    @Bean
    public org.apache.shiro.mgt.SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(myShiroRealm());
        // 自定义缓存实现 使用redis ,生产环境才需要这么设置,开发环境需要清空全选,所以不建议开启这个
        // securityManager.setCacheManager(cacheManager());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(shiroSessionAndCache.sessionManager());
        return securityManager;
    }
    @Bean
    public ZxShiroRealm myShiroRealm() {
        ZxShiroRealm myShiroRealm = new ZxShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }
    /**
     * 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     *  所以我们需要修改下doGetAuthenticationInfo中的代码; )
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }
    /**
     * 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
     * 
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        return shiroSessionAndCache.redisSessionDAO();
    }
    @Bean
    public DefaultWebSessionManager sessionManager() {
        return shiroSessionAndCache.sessionManager();
    }
    
    /**
     * 注册DelegatingFilterProxy(Shiro)
     * 集成Shiro有2种方法:
     * 1. 按这个方法自己组装一个FilterRegistrationBean(这种方法更为灵活,可以自己定义UrlPattern,
     * 在项目使用中你可能会因为一些很但疼的问题最后采用它, 想使用它你可能需要看官网或者已经很了解Shiro的处理原理了)
     * 2. 直接使用ShiroFilterFactoryBean(这种方法比较简单,其内部对ShiroFilter做了组装工作,无法自己定义UrlPattern,
     * 默认拦截 /*)
     *
     * @param dispatcherServlet
     * @return
     * @create  2016年1月13日
     */
//  @Bean
//  public FilterRegistrationBean filterRegistrationBean() {
//      FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
//      filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
//      //  该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理  
//      filterRegistration.addInitParameter("targetFilterLifecycle", "true");
//      filterRegistration.setEnabled(true);
//      filterRegistration.addUrlPatterns("/*");// 可以自己灵活的定义很多,避免一些根本不需要被Shiro处理的请求被包含进来
//      return filterRegistration;
//  }
}
@Service
public class ShiroService {
    private static Logger Logger = LoggerFactory.getLogger(ShiroService.class);
    @Autowired
    private ResourcesService resourcesService;
    /**
     * 初始化权限
     */
    public Map<String, String> loadFilterChainDefinitions() {
        // 权限控制map.从数据库获取
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/assets/**", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/font-awesome/**", "anon");
        // <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        // 自定义加载权限资源关系
        List<Resources> resourcesList = resourcesService.queryAll();
        for (Resources resources : resourcesList) {
            if (StringUtil.isNotEmpty(resources.getResurl())) {
                String permission = "perms[" + resources.getResurl() + "]";
                filterChainDefinitionMap.put(resources.getResurl(), permission);
            }
        }
        filterChainDefinitionMap.put("/**", "authc");
        Logger.info("初始化权限配置成功!");
        return filterChainDefinitionMap;
    }
}
@Component
public class ShiroSessionAndCache {
    @Autowired
    private RedisConfig redisConfig;
    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis 使用的是shiro-redis开源插件
     */
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisConfig.redisManager());
        return redisSessionDAO;
    }
    /**
     * shiro session的管理
     */
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); //session的管理,使用redis来处理session
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }
    /**
     * cacheManager 缓存 redis实现 使用的是shiro-redis开源插件
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisConfig.redisManager());
        return redisCacheManager;
    }
}
@Service
public class ShiroPermissionService {
    private static Logger Logger = LoggerFactory.getLogger(ShiroPermissionService.class);
    @Autowired
    private ShiroFilterFactoryBean shiroFilterFactoryBean;
    @Autowired
    private RedisSessionDAO redisSessionDAO;
    @Autowired
    private ShiroService shiroService;
    /**
     * 
     * 此方法描述的是:重新加载权限  刷新缓存
     * 
     * @author: Aarony
     * @version: Nov 13, 2017 10:24:25 AM
     */
    public void updatePermission() {
        synchronized (shiroFilterFactoryBean) {
            AbstractShiroFilter shiroFilter = null;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
            } catch (Exception e) {
                throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");
            }
            PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
            DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();
            // 清空老的权限控制
            manager.getFilterChains().clear();
            shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
            shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroService.loadFilterChainDefinitions());
            // 重新构建生成
            Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue().trim().replace(" ", "");
                manager.createChain(url, chainDefinition);
            }
            Logger.info("更新权限成功!");
        }
    }
}