package com.jiayue.ssi.filter;

import com.jiayue.ssi.backenum.AuditType;
import com.jiayue.ssi.backenum.BusinessStatus;
import com.jiayue.ssi.backenum.BusinessType;
import com.jiayue.ssi.constant.CacheConstants;
import com.jiayue.ssi.entity.SysBlacklist;
import com.jiayue.ssi.entity.SysOperLog;
import com.jiayue.ssi.factory.OperateLogFactory;
import com.jiayue.ssi.service.SysBlacklistService;
import com.jiayue.ssi.util.*;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

/**
 * 接口访问限制过滤器
 *
 * @author xsl
 * @since 2023/02/24
 */
@RequiredArgsConstructor
@Order(1)
@Slf4j
@Component
public class InterfaceLimitFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        response.setHeader("Strict-Transport-Security", "max-age=63072000;includeSubDomains;preload");
        response.setHeader("Content-Security-Policy","script-src 'self';object-src 'self'");
        response.setHeader("X-Content-Type-Options","nosniff");
        response.setHeader("X-XSS-Protection","1; mode=block");
        response.setHeader("X-Frame-Options","SAMEORIGIN");
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0");
        response.setHeader("Cache-Control", "no-cache='set-cookie'");
        response.setHeader("Pragma", "no-cache");

        String method = request.getMethod();
        if (!(method.toUpperCase().equals("GET") || method.toUpperCase().equals("POST"))){
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setStatus(405);
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write("方法不允许访问");
            return;
        }

        checkIp(request,response,filterChain);
    }

    synchronized void checkIp(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{
        String remoteIp = IPUtils.getIpAddr(request);
        if (CacheConstants.blacklistMap.get(remoteIp)!=null){
            SysBlacklist sysBlacklist = CacheConstants.blacklistMap.get(remoteIp);
            if ("1".equals(sysBlacklist.getType())){
                // 此ip在黑名单里类型是时间范围，则需要判断当前时间是否在范围中
                Date startDate = sysBlacklist.getLimitStartTime();
                Date endDate = sysBlacklist.getLimitEndTime();
                // 当前时间
                Date currentDate = new Date();
                if (currentDate.after(startDate) && currentDate.before(endDate)){
                    String token = request.getHeader("Authorization");
                    JwtTokenUtil jwtTokenUtil = new JwtTokenUtil();
                    Claims claims = jwtTokenUtil.getClaimsFromToken(token);
                    String username="";
                    if (claims==null){
                        username = "未知用户";
                    }
                    else{
                        username = claims.getSubject();
                    }
                    String openUrl = request.getParameter("accessUrl");
                    SysOperLog operLog = new SysOperLog();
                    operLog.setStatus(BusinessStatus.FAIL.ordinal());
                    // 请求的地址
                    String ip = IPUtils.getIpAddr();
                    operLog.setOperIp(ip);
                    operLog.setOperUrl(openUrl);
                    operLog.setOperName(username);
                    // 设置请求方式
                    operLog.setRequestMethod("");
                    operLog.setCreateBy(username);
                    // 设置action动作
                    operLog.setBusinessType(BusinessType.IPYC.ordinal());
                    // 设置标题
                    operLog.setTitle("IP异常");
                    // 操作描述
                    operLog.setOperdesc("IP异常");
                    // 审计类型
                    operLog.setAuditType(AuditType.SYS.ordinal());
                    // 保存数据库
                    OperateLogFactory.recordOper(operLog);

                    log.info("发送邮箱IP异常访问\r\n" +
                            "↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓邮箱内容↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"+
                            "\r\n  IP："+remoteIp+"异常访问系统 \r\n"     +
                            "↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑邮箱内容↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑");
//                    log.info("发送邮箱通知系统管理员后台输出======> "+"IP【"+remoteIp+"】异常访问");
                    response.setHeader("Access-Control-Allow-Origin", "*");
                    response.setStatus(410);
                    response.setContentType("text/html;charset=utf-8");
                    response.getWriter().write("IP已进黑名单，请联系管理员！");
                    return;
                }
            }
            else{
                String token = request.getHeader("Authorization");
                JwtTokenUtil jwtTokenUtil = new JwtTokenUtil();
                Claims claims = jwtTokenUtil.getClaimsFromToken(token);
                String username="";
                if (claims==null){
                    username = "未知用户";
                }
                else{
                    username = claims.getSubject();
                }
                String openUrl = request.getParameter("accessUrl");
                SysOperLog operLog = new SysOperLog();
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                // 请求的地址
                String ip = IPUtils.getIpAddr();
                operLog.setOperIp(ip);
                operLog.setOperUrl(openUrl);
                operLog.setOperName(username);
                // 设置请求方式
                operLog.setRequestMethod("");
                operLog.setCreateBy(username);
                // 设置action动作
                operLog.setBusinessType(BusinessType.IPYC.ordinal());
                // 设置标题
                operLog.setTitle("IP异常");
                // 操作描述
                operLog.setOperdesc("IP异常");
                // 审计类型
                operLog.setAuditType(AuditType.SYS.ordinal());
                // 保存数据库
                OperateLogFactory.recordOper(operLog);

                log.info("发送邮箱IP异常访问\r\n" +
                        "↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓邮箱内容↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"+
                        "\r\n  IP："+remoteIp+"异常访问系统 \r\n"     +
                        "↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑邮箱内容↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑");
                response.setHeader("Access-Control-Allow-Origin", "*");
                response.setStatus(410);
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write("IP已进黑名单，请联系管理员！");
                return;
            }
        }
        if (!InterfaceLimitUtil.checkInterface(request, 1000, 50)) {
            log.info("接口拦截：{} 请求超过限制频率【{}次/{}ms】,IP为{}", request.getRequestURI(), 50,1000, remoteIp);
            // 锁定ip黑名单
            SysBlacklist sysBlacklist = new SysBlacklist();
            sysBlacklist.setIp(remoteIp);
            sysBlacklist.setIpTime(new Date());
            sysBlacklist.setType("0");
            sysBlacklist.setAddBy("SYSTEM");
            SpringUtils.getBean(SysBlacklistService.class).save(sysBlacklist);
            // 将锁定ip加入缓存
            CacheConstants.blacklistMap.put(remoteIp,sysBlacklist);

            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setStatus(410);
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write("IP已进黑名单，请联系管理员！");
            return;
        }
        filterChain.doFilter(request, response);
    }
}
