過濾器(Filter)能夠修改HTTP請求的內容、響應、Header等信息,過濾器能夠包裝請求、響應,好比防止XSS攻擊等,過濾器一樣也能夠攔截不安全的請求,好比防止CSRF攻擊等等。html
生命週期java
Filter生命週期與Servlet生命週期相似,init()初始化Filter、destory()在銷燬時調用、doFilter()負責處理過濾響應和請求。瀏覽器
包裝響應、請求
Filter最核心的概念就是包裝請求或響應,以便它能夠執行新的行爲。Servlet提供HttpServletRequestWrapper、HttpServletResponseWrapper對象進行包裝請求和響應,使用時直接繼承便可。安全
下面是防止XSS攻擊,進行請求包裝cookie
private static class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { XssHttpServletRequestWrapper(HttpServletRequest servletRequest) { super(servletRequest); } @Override public String getHeader(String name) { return HtmlUtils.htmlEscape(super.getHeader(name)); } @Override public String getQueryString() { return HtmlUtils.htmlEscape(super.getQueryString()); } @Override public String getParameter(String parameter) { return HtmlUtils.htmlEscape(super.getParameter(parameter)); } @Override public String[] getParameterValues(String parameter) { String[] values = super.getParameterValues(parameter); if (values == null) { return null; } for (int i = 0; i < values.length; i++) { values[i] = HtmlUtils.htmlEscape(values[i]); } return values; } @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> paramMap = new HashMap<>(super.getParameterMap()); for (Map.Entry<String, String[]> entry : paramMap.entrySet()) { String[] values = entry.getValue(); String[] after = new String[values.length]; int index = 0; for (String value : values) { after[index++] = HtmlUtils.htmlEscape(value); } entry.setValue(after); } return paramMap; } } }
Spring Session就是使用Wrapper把獲取Session的API進行包裝,部分代碼以下:session
public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository); SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper( request, response, this.servletContext); SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper( wrappedRequest, response); HttpServletRequest strategyRequest = this.httpSessionStrategy .wrapRequest(wrappedRequest, wrappedResponse); HttpServletResponse strategyResponse = this.httpSessionStrategy .wrapResponse(wrappedRequest, wrappedResponse); try { filterChain.doFilter(strategyRequest, strategyResponse); } finally { wrappedRequest.commitSession(); } } }
Filter和RequestDispatcherapp
從Servlet2.4以後咱們可用使用forward()和include()進行請求分派,一樣Filter一樣能夠攔截分派的請求。cors
在配置Filter-Mapping時有
配置以下異步
<filter> <filter-name>corsFilter</filter-name> <filter-class>com.kanyuxia.servlet.chapter.filter.CrossOriginFilter</filer-class> </filter> <filter-mapping> <filter-name>corsFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> </filter-mapping>
事件監聽器可以控制ServletContext、HttpSession和ServletRequest的生命週期相關的活動
監聽器接口 | 監聽器事件 |
---|---|
ServletContextListener | ServletContextEvent |
ServletContextAttributeListener | ServletContextAttributeEvent |
HttpSessionListener | HttpSessionEvent |
HttpSessionAttributeListener | HttpSessionBindingEvent |
HttpSessionIdListener | HttpSessionEvent |
HttpSessionActivationListener | HttpSessionEvent |
HttpSessionBindingListener | HttpSessionBindingEvent |
ServletRequestListener | ServletRequestEvent |
ServletRequestAttributeListener | ServletRequestAttributeEvent |
AsyncListener | AsyncEvent |
監聽器的常見應用於其控制的相關對象的生命週期,咱們能夠基於此讓全部請求入庫
@WebListener public class AccessManager implements ServletRequestListener { @Override public void requestInitialized(ServletRequestEvent requestEvent) { ServletContext context = requestEvent.getServletContext(); ConnectionPool connectionPool = (ConnectionPool) context.getAttribute(ConnectionManager.CONNECTION_POOL_NAME); HttpServletRequest request = (HttpServletRequest)requestEvent.getServletRequest(); recordAccessLog(connectionPool, request); } @Override public void requestDestroyed(ServletRequestEvent requestEvent) {} private void recordAccessLog(ConnectionPool connectionPool, HttpServletRequest request) { // 省略部分代碼邏輯 } }
因爲HTTP是無狀態的基於請求/響應模式的協議。在構建有效的Web應用,必須與來自特定客戶端的請求彼此相互關聯,就是會話跟蹤機制。會話跟蹤機制有cookie-session、無狀態的JWT、token-session機制,這裏主要說的是cookie-session會話機制。
Cookie和Session在Servlet中如何使用就不說了,這裏主要說一下本身在應用過程當中遇到的問題
Servlet容器在接受到HTTP請求後,須要選擇合適的Servlet處理該請求。選擇的Servlet根據URL匹配==最長==上下文路徑的Servlet,其中"*"表明匹配任意字符串,最後咱們發現若是"/"會匹配任意的請求。"/"表明"default"的Servlet,Servlet容器會自動注入一個匹配路徑爲"/"的默認的Servlet,處理靜態文件獲取、404錯誤等等。
這是Tomcat9.0.2注入的默認Servlet部分代碼
public class DefaultServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Serve the requested resource, including the data content serveResource(request, response, true, fileEncoding); } /** * Serve the specified resource, optionally including the data content. */ protected void serveResource(HttpServletRequest request, HttpServletResponse response, boolean content, String inputEncoding) throws IOException, ServletException { // 省略代碼 } }