whycxzp
2022-03-14 92306778c176a9ac956f0b3be2354a44eaf7f8b2
配置文件添加系统类型,进行参数过滤和参数防篡改
2个文件已修改
4个文件已添加
317 ■■■■■ 已修改文件
src/main/java/com/whyc/constant/YamlProperties.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/controller/ServerController.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/filter/AccessFilter.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/filter/BodyReaderHttpServletRequestWrapper.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/filter/LDAPFilter.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/util/ActionUtil.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/constant/YamlProperties.java
@@ -9,6 +9,13 @@
@Component
public class YamlProperties {
    /**
     * 系统类型
     * 1:普通,没有多重校验
     * 2:严格,有多重校验(登录,防重放,参数过滤)
     */
    public static Integer systemType;
    /**人脸识别对比阈值*/
    public static Float faceThreshold;
@@ -28,6 +35,11 @@
    /**告警工单开关*/
    public static String alarmTaskSwitch;
    @Value("${system.type}")
    public void setSystemType(Integer systemType) {
        YamlProperties.systemType = systemType;
    }
    @Value("${custom.face.threshold}")
    private void setFaceThreshold(Float faceThreshold) {
        YamlProperties.faceThreshold = faceThreshold;
src/main/java/com/whyc/controller/ServerController.java
New file
@@ -0,0 +1,25 @@
package com.whyc.controller;
import com.whyc.dto.Response;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
 * 服务器相关
 */
@Api(tags = "服务器相关")
@RestController
@RequestMapping("server")
public class ServerController {
    @ApiOperation(value = "查询服务器时间戳")
    @GetMapping("timestamp")
    public Response<Long> getTimestamp(){
        return new Response<Long>().set(1,System.currentTimeMillis());
    }
}
src/main/java/com/whyc/filter/AccessFilter.java
New file
@@ -0,0 +1,85 @@
package com.whyc.filter;
import com.whyc.constant.YamlProperties;
import com.whyc.util.ActionUtil;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 防重放功能
 */
@WebFilter
public class AccessFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //加入接口防重放功能
        String time = request.getParameter("t");
        String sign = request.getParameter("sign");
        String randomStr = request.getParameter("rd");
        String requestURI = request.getRequestURI();
        //国网项目
        if (2 == YamlProperties.systemType) {
            if (time != null && sign != null && randomStr != null) { //检查接口的防重放功能
                //60秒内检查randomStr是否存在(60秒后定时清除)
                //ServletContext context = request.getServletContext();
                ServletContext context = request.getSession().getServletContext();
                //如果存在就说明参数在60秒内已经使用过了
                if (context.getAttribute(randomStr) != null) {
                    response.setStatus(403);
                    response.getWriter().write("非法请求,参数异常");
                    return;
                } else { //不存在,说明第一次使用,存入内存
                    context.setAttribute(randomStr, time);
                    context.setAttribute("randomStr_" + randomStr, time);
                }
                //60秒后,检查时效性
                if (System.currentTimeMillis() - Long.parseLong(time) >= 60 * 1000) {
                    response.setStatus(408);
                    response.getWriter().write("请求超时异常");
                    return;
                }
                boolean res = ActionUtil.checkSignMD5(time, randomStr, sign);
                if (!res) {
                    response.setStatus(403);
                    response.getWriter().write("非法请求,参数异常");
                    return;
                }
            } else {
                if (!(requestURI.contains("server/timestamp")
                        || requestURI.contains("mapOutline/all")
                        || requestURI.contains("battMapInformation/findStationState")
                        || requestURI.contains("battMapInformation/searchUserManageStation")
                        || requestURI.contains("battMapInformation/del")
                        || requestURI.contains("station3D/byDeviceId")
                        || requestURI.contains("battMapInformation/multAmout")
                        || requestURI.contains("."))) {
                    response.setStatus(403);
                    response.getWriter().write("非法请求,参数异常");
                    return;
                }
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
    @Override
    public void destroy() {
    }
}
src/main/java/com/whyc/filter/BodyReaderHttpServletRequestWrapper.java
New file
@@ -0,0 +1,58 @@
package com.whyc.filter;
import org.springframework.util.StreamUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    //保存流
    private byte[] requestBody = null;
    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        requestBody = StreamUtils.copyToByteArray(request.getInputStream());
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}
src/main/java/com/whyc/filter/LDAPFilter.java
New file
@@ -0,0 +1,105 @@
package com.whyc.filter;
import com.whyc.constant.YamlProperties;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Enumeration;
/**
 * 防止LDAP盲注
 */
@WebFilter
public class LDAPFilter implements Filter {
    private static final char[] LDAP_FORBIDDEN_REQUEST = new char[]{'\\', '*', '(', ')', '\0', '/','&','!','|'};
    private static final char[] LDAP_FORBIDDEN_BODY = new char[]{'\\', '*', '(', ')', '\0', '/','&','!','|'};
    //private static final String[] LDAP_FILTER_ESCAPE_SEQUENCE_CHARACTER = new String[]{"\\5c", "\\2a", "\\28", "\\29", "\\00", "\\2f"};
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //国网专用
        if(2 == YamlProperties.systemType) {
            String requestURI = request.getRequestURI();
            //url上的参数进行过滤
            Enumeration<String> parameterNames = request.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String paramName = parameterNames.nextElement();
                String paramValue = request.getParameter(paramName);
                if (StringUtils.isBlank(paramValue)) {
                    continue;
                }
                for (char c : LDAP_FORBIDDEN_REQUEST) {
                    int index = paramValue.indexOf(c);
                    if (index != -1) {
                        //paramValue = paramValue.replace(String.valueOf(LDAP_FILTER_ESCAPE_SEQUENCE[charIndex]), LDAP_FILTER_ESCAPE_SEQUENCE_CHARACTER[charIndex]);
                        //这个里面是允许*的,可以放过
                        if (
                                (requestURI.contains("user/") && c == '*' && (request.getMethod().toUpperCase().equals(RequestMethod.POST.name()) || request.getMethod().toUpperCase().equals(RequestMethod.PUT.name())))
                        ) {
                            continue;
                        } else if ((requestURI.contains(".servlet") || requestURI.contains("menu/menuList")) && c == '/') {
                            continue;
                        }
                        response.setStatus(403);
                        response.getWriter().write("非法请求,不允许包含特殊字符");
                        return;
                    }
                }
            }
            //body的参数进行过滤
            StringBuilder requestBody = new StringBuilder();
            String temp = "";
            //BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
            ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
            BufferedReader reader = requestWrapper.getReader();
            while ((temp = reader.readLine()) != null) {
                requestBody.append(temp);
            }
            reader.close();
            for (char c : LDAP_FORBIDDEN_BODY) {
                int index = requestBody.toString().indexOf(c);
                if (index != -1) {
                    //这个里面是允许*的,可以放过
                    if (
                            (requestURI.contains("user/") && c == '*' && (request.getMethod().toUpperCase().equals(RequestMethod.POST.name()) || request.getMethod().toUpperCase().equals(RequestMethod.PUT.name())))
                    ) {
                        continue;
                    } else if ((requestURI.contains(".servlet") || requestURI.contains("menu/menuList")) && c == '/') {
                        continue;
                    }
                    //paramValue = paramValue.replace(String.valueOf(LDAP_FILTER_ESCAPE_SEQUENCE[charIndex]), LDAP_FILTER_ESCAPE_SEQUENCE_CHARACTER[charIndex]);
                    response.setStatus(403);
                    response.getWriter().write("非法请求,不允许包含特殊字符");
                    return;
                }
            }
            filterChain.doFilter(requestWrapper,servletResponse);
        }else{
            filterChain.doFilter(request,response);
        }
    }
    @Override
    public void destroy() {
    }
}
src/main/java/com/whyc/util/ActionUtil.java
@@ -534,4 +534,36 @@
        }
        return sb.toString();
    }
    /**
     * 使用timestamp+盐方式,进行签名验证
     * 当前时间戳为13位数
     */
    public static boolean checkSignMD5(String time,String randomStr,String sign){
        String usefulNum = randomStr;
        //加盐方式,根据末尾的值进行不同的加密规则
        char lastChar = time.charAt(12);
        int lastNum = Integer.parseInt(String.valueOf(lastChar));
        switch (lastNum){
            //在第一位加字符串 rd@c3doed
            case 0: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3dozero");break;
            case 1: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3doenoe");break;
            case 2: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3doktwo");break;
            case 3: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3dolthree");break;
            case 4: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3doexfour");break;
            case 5: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3doedefive");break;
            case 6: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3doedhsix");break;
            case 7: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3doedtseven");break;
            case 8: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3doedbeight");break;
            case 9: usefulNum += time.replace(String.valueOf(lastChar),"rd@c3doedrnine");break;
        }
        //MD5加密后
        String signNow = (String) EncryptionMD5(usefulNum);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            sb.append(signNow.charAt(i*2));
        }
        String signResult = sb.append(signNow).toString();
        return sign.equals(signResult);
    }
}