項目上有個小需求,要限制訪問者的IP,屏蔽未授權的請求。該場景使用過濾器來做再合適不過了。
SecurityFilter.java:
public class SecurityFilter implements Filter { private Log log = LogFactory.getLog(SecurityFilter.class); private List<String> whitelist = new ArrayList<String>(); private List<String> regexlist = new ArrayList<String>(); private static final String _JSON_CONTENT = "application/json; charset=UTF-8"; private static final String _HTML_CONTENT = "text/html; charset=UTF-8"; private static final String _403_JSON = "{'code': '403', 'msg': '訪問被拒絕,客戶端未授權!'}"; private static final String _403_HTML = "<html><body><div style='text-align:center'><h1 style='margin-top: 10px;'>403 Forbidden!</h1><hr><span>@lichmama</span></div></body></html>"; @Override public void destroy() { } @Override public void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletrequest; HttpServletResponse response = (HttpServletResponse) servletresponse; if (isSecurityRequest(request)) { filterchain.doFilter(request, response); } else { log.info("拒絕來自[" + request.getRemoteAddr() + "]的訪問請求:" + request.getRequestURI()); response.setStatus(403); if (isAjaxRequest(request)) { response.setContentType(_JSON_CONTENT); response.getWriter().print(_403_JSON); } else { response.setContentType(_HTML_CONTENT); response.getWriter().print(_403_HTML); } } } @Override public void init(FilterConfig filterconfig) throws ServletException { String allowedIP = filterconfig.getInitParameter("allowedIP"); if (allowedIP != null && allowedIP.length() > 0) { for (String item : allowedIP.split(",\\s*")) { // 支持通配符* if (item.contains("*")) { String regex = item.replace(".", "\\.").replace("*", "\\d{1,3}"); regexlist.add(regex); } else { whitelist.add(item); } } } } /** * 判斷當前請求是否來自可信任的地址 * * @param request * @return */ private boolean isSecurityRequest(HttpServletRequest request) { String ip = request.getRemoteAddr(); for (String item : whitelist) { if (ip.equals(item)) return true; } for (String item : regexlist) { if (ip.matches(item)) return true; } return false; } /** * 判斷請求是否是AJAX請求 * @param request * @return */ private boolean isAjaxRequest(HttpServletRequest request) { String header = request.getHeader("X-Requested-With"); if (header != null && header.length() > 0) { if ("XMLHttpRequest".equalsIgnoreCase(header)) return true; } return false; } }
web.xml增加配置:
<filter> <filter-name>securityFilter</filter-name> <filter-class>com.lichmama.webdemo.filter.SecurityFilter</filter-class> <init-param> <param-name>allowedIP</param-name> <param-value>192.168.5.*</param-value> </init-param> </filter> <filter-mapping> <filter-name>securityFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
嘗試訪問,結果如下:
*如何在Filter中獲取Response的內容?這個問題之前還真沒思考過,搜索了下得知如下方法可行:
1.實現一個PrintWriterWrapper,用於替換ServletResponse中的Writer

package com.lichmama.webdemo; import java.io.PrintWriter; import java.io.Writer; public class PrintWriterWrapper extends PrintWriter { private StringBuilder buff; public PrintWriterWrapper(Writer writer) { super(writer); buff = new StringBuilder(); } @Override public void write(int i) { super.write(i); buff.append(i); } @Override public void write(char[] ac, int i, int j) { super.write(ac, i, j); buff.append(ac, i, j); } @Override public void write(char[] ac) { super.write(ac); buff.append(ac); } @Override public void write(String s, int i, int j) { super.write(s, i, j); buff.append(s, i, j); } @Override public void write(String s) { super.write(s); buff.append(s); } @Override public void flush() { super.flush(); buff.delete(0, buff.length()); } public String getContent() { return buff.toString(); } }
2.實現一個ResponseWrapper,用於替換過濾鏈(FilterChain)中的ServletResponse:

package com.lichmama.webdemo; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; public class ResponseWrapper extends HttpServletResponseWrapper { private PrintWriterWrapper writer; public ResponseWrapper(HttpServletResponse response) { super(response); } @Override public PrintWriter getWriter() throws IOException { if (writer == null) writer = new PrintWriterWrapper(super.getWriter()); return writer; } }
3.編寫Filter實現獲取Response的內容捕獲:
package com.lichmama.webdemo.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import com.lichmama.webdemo.PrintWriterWrapper; import com.lichmama.webdemo.ResponseWrapper; public class TestFilter implements Filter { @Override public void init(FilterConfig filterconfig) throws ServletException { } @Override public void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain) throws IOException, ServletException { ResponseWrapper responsewrapper = new ResponseWrapper((HttpServletResponse) servletresponse); filterchain.doFilter(servletrequest, responsewrapper); PrintWriterWrapper writerWrapper = (PrintWriterWrapper) responsewrapper.getWriter(); // TODO retrieve content from PrintWriterWrapper String content = writerWrapper.getContent(); } @Override public void destroy() { } }
that's it~