462 lines
18 KiB
Plaintext
462 lines
18 KiB
Plaintext
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<String, List<Double>> 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<String, String[]> 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<String, String> 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<String, String[]> map, String requestUrl) {
|
||
rnd = null;
|
||
if (map != null && map.size() > 0) {
|
||
Iterator<Map.Entry<String, String[]>> iterator = map.entrySet().iterator();
|
||
String value = "";// 参数值
|
||
while (iterator.hasNext()) {
|
||
value = "";
|
||
Map.Entry<String, String[]> 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<String, List<Double>>(16);
|
||
} else {
|
||
requestLogMap = (Map<String, List<Double>>) obj;
|
||
}
|
||
|
||
List<Double> list = requestLogMap.get(currentRequest);
|
||
if (list == null) {
|
||
list = new ArrayList<Double>();
|
||
} 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();
|
||
}
|
||
}
|