src/main/java/com/whyc/constant/YamlProperties.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/whyc/controller/ServerController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/whyc/filter/AccessFilter.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/whyc/filter/BodyReaderHttpServletRequestWrapper.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/whyc/filter/LDAPFilter.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/whyc/util/ActionUtil.java | ●●●●● 补丁 | 查看 | 原始文档 | 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); } }