package com.securityControl.common.security.interceptor; import com.alibaba.fastjson2.JSON; import com.github.pagehelper.util.StringUtil; import com.securityControl.common.core.constant.SecurityConstants; import com.securityControl.common.core.context.SecurityContextHolder; import com.securityControl.common.core.utils.JwtUtils; import com.securityControl.common.core.utils.ServletUtils; import com.securityControl.common.core.utils.StringUtils; import com.securityControl.common.core.utils.aes.DateTimeHelper; import com.securityControl.common.core.utils.ip.IpUtils; import com.securityControl.common.core.web.domain.AjaxResult; import com.securityControl.common.security.enums.UrlEnums; import com.securityControl.common.security.utils.SafeUtil; import com.securityControl.common.security.utils.Sm3Utils; import com.securityControl.common.security.utils.XssRequestWrapper; import com.securityControl.system.api.RemoteLogService; import com.securityControl.system.api.domain.SysOperLog; import com.sgcc.isc.framework.common.constant.Constants; import com.sgcc.isc.service.adapter.factory.AdapterFactory; import com.sgcc.isc.service.adapter.helper.IResourceService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; import org.springframework.web.servlet.AsyncHandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter; import java.util.*; /** * @author bonus * @data 2023/2/6 17:22 * @description 安全参数验证 */ @Slf4j public class ParamSecureHandler implements AsyncHandlerInterceptor { @Autowired private final PathMatcher pathMatcher = new AntPathMatcher(); //@Autow private String rnd = null; private String whiteURL ="http://10.145.34.32:21001/"; @Autowired private RemoteLogService remoteLogService; private static Map> requestLogMap = null; IResourceService resourceService = (IResourceService) AdapterFactory.getInstance(Constants.CLASS_RESOURCE); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("进入了拦截器"); System.err.println(request.getRequestURI()); if(Objects.equals("/pot/superStatistics/importExcel",request.getRequestURI()) || Objects.equals("/pot/todayTask/uploadNoticeVio",request.getRequestURI()) || Objects.equals("/pot/superStatistics/editUploadNoticeVio",request.getRequestURI()) || Objects.equals("/pot/todayTask/uploadExceptionReport",request.getRequestURI()) || Objects.equals("/pot/todayTask/uploadExceptionReport",request.getRequestURI()) || Objects.equals("/pot/superStatistics/uploadNoticeVioRect",request.getRequestURI()) || Objects.equals("/pot/device/importExcel",request.getRequestURI()) || Objects.equals("/pot/TRiskPressDropRate/importExcel",request.getRequestURI()) || Objects.equals("pot/early/exportToExcel",request.getRequestURI()) || Objects.equals("/sys/dict/getDictList",request.getRequestURI()) || Objects.equals( "/api/ballrisk/findBallUpDown",request.getRequestURI()) || Objects.equals("/api/ballrisk/findBallGb",request.getRequestURI()) || //外部接口的过滤 Objects.equals("/api/ballrisk/findDeviceStatus",request.getRequestURI()) || //外部接口的过滤 Objects.equals("/api/ballrisk/getDeviceState",request.getRequestURI()) || //外部接口的过滤 Objects.equals("/sys/menu/getAllMenuList",request.getRequestURI()) || //外部接口的过滤 request.getRequestURI().contains("/userManage/info/") || //外部接口的过滤 Objects.equals("/sys/menu/getMenuList",request.getRequestURI())){ // 过滤文件上传功能和菜单管理 return true; } if(StringUtils.isNotBlank(request.getRequestURI())){ if(request.getRequestURI().contains("/files/")){ return true; } } XssRequestWrapper requestWrapper = new XssRequestWrapper(request); String requestUrl = requestWrapper.getRequestURI(); /*if (StringUtil.isEmpty(requestUrl.trim())) { return false; }*/ /** * 防止refer篡改 */ String referUrl= request.getHeader("Referer"); if(!whiteURL.equals(referUrl)){ returnJson(response,"请求来源不正确!",500); return false; } /** * 白名单中不验证参数 */ /* boolean doFilter = isWhiteURL(requestUrl); if (doFilter) { return true; }*/ if (!requestWrapper.isChecked()) { log.error("输入值非法{}", requestWrapper.getQueryString()); returnJson(response,"输入值非法!",500); return false; } System.err.println(JSON.toJSONString(request.getParameterMap())); /** * 跨站点攻击 */ /* boolean checkXss = checkXss(request, requestUrl); if (!checkXss) { //resultError(HttpConstants.HTTP_RES_CODE_400.getKey(), ctx, "请求非法!"); throw new BindException(null, "请求非法!"); return false; }*/ /** * 获取所有跳转路径参数,保留传入下个界面 */ Map map = requestWrapper.getParameterMap(); boolean checkParameterMap = checkParameterMap(map, requestUrl); if (!checkParameterMap) { //resultError(HttpConstants.HTTP_RES_CODE_400.getKey(), ctx, "输入值非法!"); // throw new ServiceException("输入值非法!",500); returnJson(response,"输入值非法",500); return false; } /** * 同地址请求验证 */ String ckval = request.getHeader("ckval"); String currentRequest = request.getRemoteAddr() + ":" + requestUrl + "?ckval=" + ckval; /* boolean checkSameUrl = checkSameUrl(currentRequest, requestWrapper); if (!checkSameUrl) { log.info("{}请求重复,{}", currentRequest, rnd); //resultError(HttpConstants.HTTP_RES_CODE_400.getKey(), ctx, "请求重复!"); throw new ServiceException("请求重复!"); }*/ /** * 检查数据流参数 */ String readerParam = requestWrapper.getReaderParam(); if (requestUrl.indexOf("uploadFile") < 0 && requestUrl.indexOf("uploadImage") < 0) {// 判断是否是文件上传,是不对流参数进行验证 boolean checkReader = checkReader(readerParam, requestUrl); if (!checkReader) { //resultError(HttpConstants.HTTP_RES_CODE_400.getKey(), ctx, "输入值非法!"); // throw new GlobalException("请求重复!"); returnJson(response,"请求重复",500); return false; } } if (!sm3Check(request)) { // throw new GlobalException("请求参数丢失!"); returnJson(response,"请求参数丢失",500); return false; } /*if(!checkIsYq(request,requestWrapper)){ // throw new ServiceException("请求越权,请检查用户权限!"); returnJson(response,"请求越权,请检查用户权限",500); return false; }*/ return true; } private void returnJson(HttpServletResponse response,String msg,int code){ PrintWriter writer=null; response.setCharacterEncoding("UTF-8"); response.setContentType("applicatiopn/json;charset=utf-8"); AjaxResult a=AjaxResult.error(code,msg); String res=JSON.toJSONString(a); try { writer=response.getWriter(); writer.println(res); }catch (IOException e){ e.printStackTrace(); } } /** * 判断是否越权 */ private boolean checkIsYq(HttpServletRequest request,XssRequestWrapper requestWrapper) throws Exception { String requestURI = request.getRequestURI(); String[] headUrls = requestURI.split("/"); String url = "/" + headUrls[1]+"/" + headUrls[2] + "/**"; Boolean result = true; String token = requestWrapper.getParameter("token"); if (StringUtils.isNotEmpty(token)) { String userId = JwtUtils.getIscUserId(token); System.out.println("拦截器userId:" + userId); if (StringUtil.isEmpty(userId)) { result = false; } else { result = resourceService.hasPermitURLObj(userId, "9b4483c383538275018615493e1451ea", url); } System.out.println("==================越狱记录:========================userId:" + userId + "============是否越狱:" + result); } else { result = false; } if (!result) { //记录日志 鹏飞 addExceedsAccessLog(url, token); return false; //添加弹框 } return true; } private void addExceedsAccessLog(String url, String token) { SysOperLog sysOperLog = new SysOperLog(); sysOperLog.setGrade("越权访问"); sysOperLog.setOperName(JwtUtils.getUserName(token)); sysOperLog.setTimes(DateTimeHelper.getNowTime()); sysOperLog.setRoleName("继远管理员"); sysOperLog.setDeptName("建设分公司"); sysOperLog.setOperIp(IpUtils.getIpAddr(ServletUtils.getRequest())); UrlEnums[] enums = UrlEnums.values(); for (UrlEnums anEnum : enums) { if (url.startsWith(anEnum.getUrl())) { sysOperLog.setTitle(anEnum.getInfo()); } } if (StringUtils.isEmpty(sysOperLog.getTitle())) { sysOperLog.setTitle("系统管理"); } sysOperLog.setRequestMethod(""); sysOperLog.setMethod(""); sysOperLog.setBusinessType(1); sysOperLog.setOperUrl(""); sysOperLog.setOperParam(""); sysOperLog.setDetail("用户越权访问地址:" + url); sysOperLog.setLogType("系统日志"); sysOperLog.setSysMenu(""); sysOperLog.setStatus(1); remoteLogService.saveLogs(sysOperLog, SecurityConstants.INNER); } /** * 简单的xss跨站点检查 * * @param request * @param requestUrl * @return */ /*private boolean checkXss(HttpServletRequest request, String requestUrl) { String host = request.getHeader("Host"); String localAddr = foundationConfig.getRequestUrl().getLocalAddr();//request.getLocalAddr(); String remoteHost = foundationConfig.getRequestUrl().getRemoteHost();//request.getRemoteHost(); String referer = request.getHeader("Referer"); log.debug("获取到host:{},localAddr:{},remoteHost:{},referer:{}", host, localAddr, remoteHost, referer); if (StringUtil.isEmpty(host) || StringUtil.isEmpty(localAddr) || StringUtil.isEmpty(remoteHost) || StringUtil.isEmpty(referer)) { return false; } // 如果请求地址为空则直接返回false if (requestUrl == null) { log.info("当前请求地址不存在"); return false; } String localAddr2 = request.getLocalAddr(); String remoteHost2 = request.getRemoteHost(); if (referer.indexOf(remoteHost) == -1 && referer.indexOf(remoteHost2) == -1) { return false; } if (host.indexOf(localAddr) == -1 && host.indexOf(localAddr2) == -1) { return false; } // 逻辑: 判断SERVICE_TYPES数组中 所有元素 是否有一个能够通过验证 // 循环SERVICE_TYPES // 判断当前变量是否存在于referer,判断当前变量在不存在于referer,是否存在于requestUrl // 存在则返回true,结束循环 // 不存在,返回false,提示当前来源不是本站请求 for (String headerURL : foundationConfig.getHeaderUrls()) { if (referer.indexOf(headerURL) != -1 || requestUrl.indexOf(headerURL) != -1) { return true; } } log.info("当前请求来源不是本站请求!请求地址:" + requestUrl + ".请检查请求的URL是否正确!"); return false; }*/ /* */ /** * 判断白名单 * * @param currentURL * @return */ private boolean isWhiteURL(String currentURL) { // for (String whiteURL : foundationConfig.getWhiteUrls()) { if (pathMatcher.match(whiteURL, currentURL)) { log.info("白名单过滤: [{}] 匹配 [{}] ", whiteURL, currentURL); return true; } log.info("白名单过滤: [{}] 未匹配 [{}]", whiteURL, currentURL); // } return false; } /** * 参数校验 * * @param request * @return */ private boolean sm3Check(HttpServletRequest request) { Map map = new LinkedHashMap<>(); request.getParameterMap().forEach((key, value) -> { if (!Objects.equals(key, "token") ) { map.put(key, String.join(" ", value)); } }); String header = request.getHeader("encrypt"); String json = JSON.toJSONString(map); request.getParameterMap(); String str = Sm3Utils.encrypt(json); return str.equalsIgnoreCase(header); } /** * 检查所有页面输入的参数是否安全 request.getParameterMap() * * @param map * @param requestUrl */ private boolean checkParameterMap(Map map, String requestUrl) { rnd = null; if (map != null && map.size() > 0) { Iterator> iterator = map.entrySet().iterator(); String value = "";// 参数值 while (iterator.hasNext()) { value = ""; Map.Entry entry = iterator.next(); String pname = (String) entry.getKey(); // 参数名 if (Objects.equals("token", pname)) { continue; } Object objValue = entry.getValue(); // 数组 if (null == objValue) { value = ""; } else if (objValue instanceof String[]) { String[] values = (String[]) objValue; for (int i = 0; i < values.length; i++) { value = values[i] + ","; } if (value.length() > 0) { value = value.substring(0, value.length() - 1); } } if ("rnd".equals(pname)) { rnd = value; } if (SafeUtil.checkSafeSql(value)) { log.info("请求失败,当前请求参数不安全!请求地址:\n" + requestUrl + "\n不安全参数:" + pname + ":" + value); return false; } if (SafeUtil.checkSpecial(value)) { log.info("请求失败,当前请求参数包含特殊字符!请求地址:\n" + requestUrl + "\n特殊字符参数:" + pname + ":" + value); return false; } } } return true; } /** * 通过rnd随机参数判断相同url是否多次请求访问 * * @param currentRequest * @param requestWrapper * @return */ private boolean checkSameUrl(String currentRequest, XssRequestWrapper requestWrapper) { if (rnd != null && rnd.length() > 0) { try { double newRnd = Double.parseDouble(rnd); /** * 当为空时,新增请求时间记录,第一次请求,放行,允许查询 */ HttpSession session = requestWrapper.getSession(); Object obj = session.getAttribute("requestLogMap"); if (obj == null) { requestLogMap = new HashMap>(16); } else { requestLogMap = (Map>) obj; } List list = requestLogMap.get(currentRequest); if (list == null) { list = new ArrayList(); } else { for (Double oldRnd : list) { if (oldRnd == newRnd) { log.info("请求失败,当前请求已过期!请重新登录!您的请求地址:\n" + currentRequest); return false; } } } list.add(newRnd); requestLogMap.put(currentRequest, list); if (session.getAttribute("requestLogMap") != null) { session.removeAttribute("requestLogMap"); } session.setAttribute("requestLogMap", requestLogMap); } catch (NumberFormatException e) { log.info("rnd参数格式化错误 请检查"); return false; } } else { log.info("请求失败,rnd参数不存在!请重新登录!您的请求地址:\n" + currentRequest); return true; } return true; } /** * 检查所有页面输入的参数是否安全 request.getReader() * * @param readerParam * @param requestUrl */ private boolean checkReader(String readerParam, String requestUrl) { if (SafeUtil.checkScript(readerParam)) { log.info("请求失败,当前请求参数不安全!请求地址:\n" + requestUrl + "\n不安全参数:数据流:" + readerParam); return false; } return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { SecurityContextHolder.remove(); } }