package com.gci.boot.config;

import com.gci.boot.modules.system.shiro.authc.ShiroRealm;
import com.gci.boot.modules.system.util.JwtUtil;
import com.gci.boot.properties.GciSecurityProperties;
import com.gci.boot.modules.system.service.ISysUserService;
import com.gci.boot.modules.system.shiro.authc.CustomShiroFilterFactoryBean;
import com.gci.boot.modules.system.shiro.authc.aop.JwtFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
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.filter.InvalidRequestFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import javax.servlet.Filter;
import java.util.*;

/**
 * @author: Scott
 * @date: 2018/2/7
 * @description: shiro 配置类
 */

@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Slf4j
@Configuration
@EnableConfigurationProperties(GciSecurityProperties.class)
public class ShiroConfig {


    @Autowired
    private GciSecurityProperties gciSecurityProperties;

    /**
     * Filter Chain定义说明
     *
     * 1、一个URL可以配置多个Filter，使用逗号分隔
     * 2、当设置多个过滤器时，全部验证通过，才视为通过
     * 3、部分过滤器可指定参数，如perms，roles
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        CustomShiroFilterFactoryBean shiroFilterFactoryBean = new CustomShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        String excludeUrls = gciSecurityProperties.getShiro().getExcludeUrls();

        if(StringUtils.isNotBlank(excludeUrls)){
            String[] permissionUrl = excludeUrls.split(",");
            for(String url : permissionUrl){
                /*
                    添加shiro的内置过滤器
                    anon：无需认证就可以访问
                    authc：必须认证才可以访问
                    user：必须有“记住我”功能才能使用
                    perms：拥有对某个资源的权限才能访问
                    role：拥有某个角色权限才能访问
                 */
                filterChainDefinitionMap.put(url,"anon");
            }
        }

        // 添加自己的过滤器并且取名为jwt
        Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
        filterMap.put("jwt", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        // <!-- 过滤链定义，从上向下顺序执行，一般将/**放在最为下边
        if(!filterChainDefinitionMap.containsKey("/**")){
            filterChainDefinitionMap.put("/**", "jwt");
        }

        // 未授权界面返回JSON
        /*shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setLoginUrl("/error");*/
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean("securityManager")
    public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);

        /*
         * 关闭shiro自带的session，详情见文档
         * http://shiro.apache.org/session-management.html#SessionManagement-
         * StatelessApplications%28Sessionless%29
         */
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        //自定义缓存实现,使用redis
        return securityManager;
    }

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

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

    @Bean
    public InvalidRequestFilter invalidRequestFilter(){
        InvalidRequestFilter invalidRequestFilter = new InvalidRequestFilter();
        invalidRequestFilter.setBlockNonAscii(false);
        return invalidRequestFilter;
    }

    @Bean
    public ShiroRealm shiroRealm(@Lazy ISysUserService sysUserService){
        return new ShiroRealm(sysUserService, gciSecurityProperties.getShiro().getExpireTime());
    }

    @Bean
    public JwtUtil jwtUtil(){
        JwtUtil jwtUtil = new JwtUtil();
        jwtUtil.setExpireTime(gciSecurityProperties.getShiro().getExpireTime());
        if(StringUtils.isNotBlank(gciSecurityProperties.getJwt().getSecret())){
            jwtUtil.setSecret(gciSecurityProperties.getJwt().getSecret());
        }
        return jwtUtil;
    }
}
