package com.njcn.fliter;

import com.njcn.shiro.filter.ShiroFilterUtils;
import com.njcn.utils.XssFilterUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * @author hongawen
 * @Date: 2019/2/13 10:39
 */
public class XssFilter implements Filter {

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

    /**
     * 排除部分URL不做过滤
     */
    private List<String> excludeUrls = new ArrayList<>();

    /**
     * 排除部分变量参数不做过滤
     */
    private List<String> excludeParams = new ArrayList<>(Arrays.asList("series","key","reportName","charData", "deptspic", "deptsevent", "voltagepic", "voltageevent", "sagpic", "densityImage", "iticImage", "f47Image", "eventValueImage", "persisttimeImage", "timeImage", "causeImage", "typeImage", "devmapImage", "intervalImage", "districtImage", "voltageImage", "eventmapImage","password","xml","svg"));

    /**
     * 公告新增、修改用到富文本，对标签进行转义
     */
    private List<String> noticeUrls = new ArrayList<>();

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;
        String pathInfo = req.getPathInfo() == null ? "" : req.getPathInfo();
        String url = req.getServletPath() + pathInfo;
        // 获取请求所有参数，校验防止SQL注入，防止XSS漏洞
        Enumeration<?> params = req.getParameterNames();
        String paramN;
        Map paramter = new HashMap(16);
        while (params.hasMoreElements()) {
            paramN = (String) params.nextElement();
            if(paramN.contains("[]")){
                String[] index=req.getParameterValues(paramN);
                paramter.put(paramN, index);
            }else{
                String paramVale = req.getParameter(paramN);
                if (excludeParams.contains(paramN)) {
                    logger.info("该参数不作校验：" + paramN);
                    filterChain.doFilter(req, response);
                    return;
                }
                //否则所有的参数的值都要进行参数值过滤
                paramVale =XssFilterUtil.xssEncode(paramVale);
                // 校验是否存在SQL或者script注入信息
                if (checkSQLInject(paramN, paramVale, url)) {
                    errorResponse(req, response, paramN);
                    return;
                }
                 paramter.put(paramN, paramVale);
            }
        }
        ParameterRequestWrapper wrapper = new ParameterRequestWrapper(req, paramter);
        filterChain.doFilter(wrapper, response);
        //判断白名单参数名
        // 排除部分URL不做过滤。
//        for (String str : excludeUrls) {
//            if (uri.indexOf(str) >= 0) {
//                logger.info("该URL不作校验：" + url);
//                arg2.doFilter(req, response);
//                return;
//            }
//        }
//        for (String st : noticeUrls) {
//            if (uri.indexOf(st) >= 0) {
//                isNoticeUrl = true;
//                break;
//            }
//        }
    }

    private void errorResponse(HttpServletRequest request, HttpServletResponse response, String paramNm) throws IOException {
        if (ShiroFilterUtils.isAjax(request)) {
            response.sendError(501);
            ShiroFilterUtils.out(response, "输入项中不能包含非法字符");
        } else {
            String url = request.getRequestURL().toString();
            Integer port = request.getLocalPort();
            String addr = url.substring(0, url.indexOf(port.toString()));
            url = addr + port + "/sso/auth/urlXssError";
            WebUtils.issueRedirect(request, response, url);
        }
    }

    @Override
    public void destroy() {
    }

    /***
     * 初始化
     */
    @Override
    public void init(FilterConfig filterconfig1) throws ServletException {
        // 读取文件
//        String path = XssFilter.class.getResource("/").getFile();
//        excludeUrls = readFile(path + "xssWhite.txt");
//        noticeUrls.add("notice!saveNotice");
//        noticeUrls.add("notice!updateNoticeById");
    }

    /**
     * 读取白名单
     *
     * @param fileName 文件名
     */
    private List<String> readFile(String fileName) {
        List<String> list = new ArrayList<String>();
        BufferedReader reader = null;
        FileInputStream fis = null;
        try {
            File f = new File(fileName);
            if (f.isFile() && f.exists()) {
                fis = new FileInputStream(f);
                reader = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
                String line;
                while ((line = reader.readLine()) != null) {
                    if (!"".equals(line)) {
                        list.add(line);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("readFile", e);
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                logger.error("InputStream关闭异常", e);
            }
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                logger.error("FileInputStream关闭异常", e);
            }
        }
        return list;
    }

    /**
     * 检查是否存在非法字符，防止SQL注入
     *
     * @param str 被检查的字符串
     * @return ture-字符串中存在非法字符，false-不存在非法字符
     */
    public static boolean checkSQLInject(String paramN, String str, String url) {
        if (StringUtils.isEmpty(str)) {
            return false;// 如果传入空串则认为不存在非法字符
        }
        str = str.toLowerCase(); // sql不区分大小写
        // 判断黑名单
        String[] inj_stra = {"script", "truncate", "insert", "select", "delete", "update", "declare", "iframe", "alert", "xss","onClick", "\\",  "confirm", "prompt", "onload", "onmouseover", "onfocus", "onerror"};
        for (int i = 0; i < inj_stra.length; i++) {
            if (str.toUpperCase().contains(inj_stra[i].toUpperCase())) {
                logger.info("xss防攻击拦截url:" + url + "，原因：特殊字符，传入str=" + str + ",包含特殊字符：" + inj_stra[i]);
                return true;
            }
        }
        return false;
    }
}
