涉及的場景
- session校驗
比如,部分系統必須保證系統登錄之後才能正常使用,登錄之後會將登錄信息保存在session中,因此可以在Filter實現session數據的校驗 - 請求攔截
如果平臺涉及到黑白名單相關的機制,可以使用Filter實現攔截相關請求,並響應異常。 - 平臺級的數據檢驗
當平臺的所有接口都需要按一定的協議進行加密或者驗籤,可以通過Filter獲取到請求的數據,校驗是否符合加密規則或者驗籤規則,符合通過,不符合拒絕 - 削峯限流
防止突發的併發請求擊垮整個服務,可以考慮在Filter中使用限流機制削減洪峯,保障服務可用性的同時給用戶一個友好的提示。
測試示例
-
請求數據幫助類
用戶Post請求時通過Stream獲取請求數據import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; public class HttpGetBodyInfoHelper { public static String getBodyString(HttpServletRequest request) { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } }
-
定一個RequestWrapper包裝器
由於body中的數據是以I/O流的形式發送給服務端的,因此在服務端讀取的時候,如果不使用包裝器將讀取出來的數據包裝之後傳給下一個處理器,那麼在下一個處理器處理的時候,將無法讀取到數據;因爲前一個處理器處理數據的時候讀取過了ByteBuffer,此時ByteBuffer中的postion和limit指針指向了同一個位置,因此無法再次讀取數據;通過包裝器對象,可以將請求數據傳遞給下一個處理器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; import java.nio.charset.Charset; public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; private String bodyStr; public RequestReaderHttpServletRequestWrapper(HttpServletRequest request) { super(request); //獲取前端傳遞的參數 this.bodyStr = HttpGetBodyInfoHelper.getBodyString(request); //將文本數據轉成數組對象 body = bodyStr.getBytes(Charset.forName("UTF-8")); } //封裝一個返回文本數據的對象 public String getBody() { return bodyStr; } @Override public BufferedReader getReader() { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() { //將數據流傳給下一個處理器 //因爲這裏讀取了數據,流數據讀取一次之後就沒有了,因此這裏以流的形式將數據再次會給下一個處理器 final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() { return bais.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } }
-
定義Filter
以下示例了初始化參數的獲取、session的操作、cookie的操作、ip的獲取、get/post請求數據獲取等,當拿到這些數據之後,我們就可以根據自己業務的需要,對這些數據進行處理import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpMethod; import javax.servlet.*; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; public class ServiceCheckFilter implements Filter { private Map<String, String> initParameters = new HashMap<>(); @Override public void init(FilterConfig filterConfig) throws ServletException { //用於初始化基礎參數使用 //初始化時候的參數 Enumeration<String> initParameterNames = filterConfig.getInitParameterNames(); //遍歷鏈表 while (initParameterNames.hasMoreElements()) { //得到鏈表的key String key = initParameterNames.nextElement(); //取到對應的值 String va = filterConfig.getInitParameter(key); //判斷是否是配置不攔截的key value的值 if (StringUtils.isNotEmpty(va)) { //使用;將值切分成數組 initParameters.put(key, va); System.out.println("init方法得到的自定義值,key:" + key + " va:" + va); } } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (request instanceof HttpServletRequest) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; //獲取到session HttpSession httpSession = httpServletRequest.getSession(); //設置session的值 httpSession.getAttribute("sessionKey"); //設置session的值 httpSession.setAttribute("sessionKey", "sessionValue"); //刪除session中對應key的值 httpSession.removeAttribute("sessionKey"); //設置session超時時間 httpSession.setMaxInactiveInterval(60); Cookie[] cookies = httpServletRequest.getCookies(); if (null != cookies) { for (Cookie cookie : cookies) { //操作cookie數據 } } System.out.println("getRequestURI()-->" + httpServletRequest.getRequestURI()); System.out.println("getRequestURL().toString()-->" + httpServletRequest.getRequestURL().toString()); System.out.println("getQueryString()-->" + httpServletRequest.getQueryString()); System.out.println("getRemoteAddr()-->" + httpServletRequest.getRemoteAddr()); System.out.println("getRemoteHost()-->" + httpServletRequest.getRemoteHost()); System.out.println("getRemotePort()-->" + httpServletRequest.getRemotePort()); //獲取請求方式 String method = httpServletRequest.getMethod(); //轉爲HttpMethod枚舉 HttpMethod httpMethod = HttpMethod.valueOf(method); //定義請求包裝器,便於將數據傳遞給下一個處理器 RequestReaderHttpServletRequestWrapper requestWrapper = null; switch (httpMethod) { case GET: //當前請求是get方式 Enumeration<String> parameterNames = httpServletRequest.getParameterNames(); while (parameterNames.hasMoreElements()) { String key = parameterNames.nextElement(); String va = httpServletRequest.getParameter(key); System.out.println("get請求獲取數據Key:" + key + " va:" + va); } //可以根據自己的需要進行業務校驗,如果不符合業務要求,可以根據個人的情況返回錯誤 //if(1==1) { // interceptMsg(response, "{'code':-1,'msg':'出現錯誤!'}"); // return; //} break; case POST: //當前請求是post //這裏需要實例化一個requestWrapper包裝器,將數據包裝起來,便於傳遞給下一個Filter //記住,這裏務必要使用一個包裝器,因爲Post需要通過Stream去獲取數據,緩存區的數據只能夠讀取一遍, // 如果不使用包裝器包裝的話,在這裏獲取一遍之後,下一個Filter在處理的時候就沒辦法在request中read到數據了 requestWrapper = new RequestReaderHttpServletRequestWrapper(httpServletRequest); //得到請求數據 String body = requestWrapper.getBody(); System.out.println("post請求獲取數據:" + body); //得到請求數據進行校驗 break; } if (null == requestWrapper) { chain.doFilter(request, response); } else { chain.doFilter(requestWrapper, response); } } } /** * 返回數據 * * @param response * @param json */ private void interceptMsg(ServletResponse response, String json) { PrintWriter writer = null; response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); try { writer = response.getWriter(); writer.print(json); } catch (IOException e) { System.out.println("寫數據出現異常"); } finally { if (writer != null) writer.close(); } } @Override public void destroy() { //過濾器銷燬的時候 執行相關收尾工作 //清楚所有的數據 initParameters.clear(); } }
-
註冊Filter
以下是SpringBoot的方式@Bean public FilterRegistrationBean signCheckFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); //設置過濾器 registration.setFilter(new ServiceCheckFilter()); //匹配攔截的Url registration.addUrlPatterns("/*"); //這個可以根據自己的業務場景初始化數據,在Filter對象初始化的時候會調用init(config)方法將數據傳遞過去 registration.addInitParameter("initData", "initVa"); //設置別名 registration.setName("signCheckFilter"); //設置註冊的順序,因爲可能存在多個Filter registration.setOrder(1); return registration; }
-
獲取用戶的真是IP
上面雖然列舉了獲取IP的方式,但是如果用戶使用了代理之後,獲取的就是代理的IP,可以通過以下方式獲取到用戶的正式IPimport javax.servlet.http.HttpServletRequest; public class ClientIPUtil { /** * 獲取用戶真實IP地址,不使用request.getRemoteAddr();的原因是有可能用戶使用了代理軟件方式避免真實IP地址, * <p> * 可是,如果通過了多級反向代理的話,X-Forwarded-For的值並不止一個,而是一串IP值,究竟哪個纔是真正的用戶端的真實IP呢? * 答案是取X-Forwarded-For中第一個非unknown的有效IP字符串。 * <p> * 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130, * 192.168.1.100 * <p> * 用戶真實IP爲: 192.168.1.110 * * @param request * @return */ public static String getIpAddress(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } }