隨筆 - 41  文章 - 7  trackbacks - 0
          <2016年7月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          Servlets定義為JSR 340,可以下載完整規范.
          servlet是托管于servlet容器中的web組件,并可生成動態內容.web clients可使用請求/響應模式同servlet交互. 
          servlet容器負責處理servlet的生命周期事件,接收請求和發送響應,以及執行其它必要的編碼/解碼部分.
          WebServlet
          它是在POJO上使用@WebServlet注解定義,且必須繼承javax.servlet.http.HttpServlet類的servlet.
          這里有一個示例servlet定義:
          @WebServlet("/account")
          public class AccountServlet extends javax.servlet.http.HttpServlet {
          //. . .
          }
          默認的servlet名稱是全限定類名,可使用注解的name屬性來進行覆蓋. servlet可使用多個URL來部署:
          @WebServlet(urlPatterns={"/account""/accountServlet"})
          public class AccountServlet extends javax.servlet.http.HttpServlet {
          //. . .
          }
          @WebInitParam 可用來指定初始化參數:
          @WebServlet(urlPatterns="/account",
          initParams={
          @WebInitParam(name="type"value="checking")
          }
          )
          譯者注:通過注解方式來編寫Servlet,不需要在web.xml中定義
          public class AccountServlet extends javax.servlet.http.HttpServlet {
          //. . .
          }
          HttpServlet 接口包含doXXX 相應的方法來處理HTTP GET, POST,PUT, DELETE, HEAD, OPTIONS, 以及 TRACE請求. 通常開發者比較關心的是覆蓋doGet和doPost方法.下面的代碼展示了一個用于處理GET請求的servlet:
          @WebServlet("/account")
          public class AccountServlet extends javax.servlet.http.HttpServlet {
          @Override
          protected void doGet(
          HttpServletRequest request,
          HttpServletResponse response) {
          //. . .
          }
          }
          在這個代碼中:
          • HttpServletRequest和HttpServletResponse用于捕獲web client的請求/響應.
          • 請求參數,HTTP headers; 路徑的不同部分如host,port, 以及context以及更多的信息都可以從HttpServletRequest中獲取.
          HTTP cookies可以發送和獲取.開發者需要負責填充(populating)HttpServletResponse, 然后容器傳送捕獲的HTTP  headers,消息主體(message body)給client.
          下面的代碼展示了servlet接收到HTTP  GET 請求后,如何向client顯示簡單響應的:
          protected void doGet(HttpServletRequest request,HttpServletResponse response) {
          try (PrintWriter out response.getWriter()) {
          out.println("<html><head>");
          out.println("<title>MyServlet</title>");
          out.println("</head><body>");
          out.println("<h1>My First Servlet</h1>");
          //. . .
          out.println("</body></html>");
          finally {
          //. . .
          }
          }
          請求參數可在GET和POST請求中傳遞.在 GET請求中,這些參數是以查詢字符串的name/value對進行傳遞的.這里有一個使用請求參數調用先前servlet的示例URL:
          . . ./account?tx=10
          在POST請求中,請求參數也可以通過在請求體中編碼的posted數據來傳遞.在GET和POST請求中,這些參數都可從HttpServletRequest中獲取:
          protected void doGet(HttpServletRequest request,HttpServletResponse response) {
          String txValue request.getParameter("tx");
          //. . .
          }
          每個請求中的請求參數都可以不同.
          Initialization parameter(初始化參數), 也稱為init params,可在servlet啟動和配置信息中定義.
          正如先前說明的, @WebInitParam用來指定servlet的初始化參數:
          String type null;
          @Override
          public void init(ServletConfig configthrows ServletException {
          type config.getInitParameter("type");
          //. . .
          }
          通過覆蓋javax.servlet.Servlet接口的init,service,destroy方法,你可以操縱servlet生命調用方法的默認行為. 典型地,數據庫連接在init方法中初始化,在destroy方法中釋放.
          你也可以在web程序的部署描述文件web.xml中使用servlet和servlet-mapping來定義一個servlet.
          你可以在web.xml中定義Account Servlet:
          <?xml version="1.0" encoding="UTF-8"?>
          <web-app version="3.1"
          xmlns="http://xmlns.jcp.org/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
          http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
          <servlet>
          <servlet-name>AccountServlet</servlet-name>
          <servlet-class>org.sample.AccountServlet</servlet-class>
          </servlet>
          <servlet-mapping>
          <servlet-name>AccountServlet</servlet-name>
          <url-pattern>/account</url-pattern>
          </servlet-mapping>
          </web-app>
          注解已覆蓋了大多數常見情況,因此在那些情況下不需要web.xml。但在某些情況下,如要設置servlets的順序,則只能使用web.xml完成(后面有講解).
          如果web.xml中的metadata-complete元素為true,那么類中的注解將不再處理(譯者注:即忽略注解,此時servlet必須在web.xml中配置):
          <web-app version="3.1"
          xmlns="http://xmlns.jcp.org/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
          http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
          metadata-complete="true">
          //. . .
          </web-app>
          在部署描述符中定義的值將覆蓋注解中定義的值.
          譯者注:重要的事,再說一次,如果設置了 metadata-complete="true",而在web.xml中又沒有配置servlet的url映射的話,訪問servlet時會出現404錯誤.切記,切記.
          在web程序中,servlet是打包在 .war 文件中的.多個servlets可以打包在一起,這樣它們可共享一個servlet context. ServletContext提供了關于servlets執行環境的信息,常常用來與容器通信—例如,在web程序中讀取資源包,寫日志文件或轉發請求.
          ServletContext可從HttpServletRequest中獲取:
          protected void doGet(HttpServletRequest request,HttpServletResponse response) {
          ServletContext context request.getServletContext();
          //. . .
          }
          為了會話追蹤,servlet可向client發送名為JSESSIONID的HTTP cookie. 此 cookie 可被標記為HttpOnly,這樣可確保cookie不會暴露于客戶端腳本代碼,因此可幫助減輕某種形式的跨站點腳本攻擊(注意這里只是減輕,并不能完全杜絕):
          SessionCookieConfig config request.getServletContext().
          getSessionCookieConfig();
          config.setHttpOnly(true);
          另外,Servlet可使用URL重寫來作為一個基礎的會話跟蹤。
          ServletContext.getSessionCookieConfig 方法會返回SessionCookieConfig, 它可以用來配置cookie的不同屬性.
          HttpSession 接口可用來查看關于會話標識符和創建時間的相關信息,也可用來綁定對象.
          可用來創建新的session對象:
          protected void doGet(HttpServletRequest request,
          HttpServletResponse response) {
          HttpSession session request.getSession(true);
          //. . .
          }
          session.setAttribute 和session.getAttribute方法用來在session上綁定對象.
          如果需要更一步的處理,servlet可將請求轉發它其它請求.
          你可通過使用RequestDispatchercan轉發請求來達到此目的 , RequestDispatchercan可通過HttpServletRequest.getRequestDispatcher或 ServletContext.getRequestDispatcher獲?。?nbsp;前者只接受相對路徑,而后者可以接受一個相對于當前上下文的路徑:
          protected void doGet(HttpServletRequest request,
          HttpServletResponse response) {
          request.getRequestDispatcher("bank").forward(requestresponse);
          //. . .
          }
          在上面的代碼中,bank是部署在相同上下文的另一個servlet.
          ServletContext.getContext 方法可用來獲取外部環境的ServletContext. 然后它可以用來獲取一個RequestDispatcher,這樣它就可以將請求轉發到那個上下文中了.
          通過調用 HttpServletResponse.sendRedirect方法,你可重定向servlet響應到其它資源.這會向client發送一個臨時重定向響應,然后client再發出一個指定URL的新請求. 注意,在這種情況下,原始請求對象對于重定向URL是不可用的. 重定向(redirect)可能稍為會慢點,因為它需要兩個客戶端請求,而轉發(forward)是在容器內進行:
          protected void doGet(HttpServletRequest request,
          HttpServletResponse response) {
          //. . .
          response.sendRedirect("http://example.com/SomeOtherServlet");
          }
          這里,響應被重定向到了http://example.com/SomeOtherServlet URL.注意,這里的URL可以是不同主機/端口,也可以是容器相對的或絕對的路徑.
          除了使用@WebServlet和web.xml聲明serlvet,你也可以通過編程使用ServletContext.addServlet方法來定義. 你可從ServletContainerInitializer.onStartup或ServletContextListener.contex
          tInitialized方法中來做到這一點.在17頁"Event Listeners”中,你可以讀到更多關于這些的信息.
          ServletContainerInitializer.onStartup 方法是在程序啟動的時候調用的.addServlet方法將返回
          ServletRegistration.Dynamic, 然后就可以用它(ServletRegistration.Dynamic)來創建URL映射,設置安全角色,設置初始化參數,以及管理其它配置項:
          public class MyInitializer implements ServletContainerInitializer {
          @Override
          public void onStartup (Set<Class<?>> clazzServletContext context) {
          ServletRegistration.Dynamic reg =
          context.addServlet("MyServlet""org.example.MyServlet");
          reg.addMapping("/myServlet");
          }
          }
          Servlet 過濾器
          servlet過濾器可用來修改請求和響應的負載以及頭信息. 重要的是,要意識到過濾器不會創建響應—它們只修改或適配請求和響應. 認證,日志,數據壓縮,數據加密通常都通過過濾器來實現. 過濾器同servlet一起打包,它可作用于動態或靜態內容.
          通過指定一種URL模式,過濾器可與servlet或一組servlet以及靜態內容相關聯. 可使用@WebFilter注解來定義過濾器:
          @WebFilter("/*")
          public class LoggingFilter implements javax.servlet.Filter {
          public void doFilter(HttpServletRequest request,
          HttpServletResponse response) {
          //. . .
          }
          }
          在上面展示的代碼中,LoggingFilter將應用于web程序中所有servlets和靜態內容頁面上.
          @WebInitParam 也可在這里指定初始化參數.
          過濾器和目標servlet總是在同一個調用線程中執行. 多個過濾器可在過濾器鏈中安排執行.
          在部署描述符中,你也可以使用<filter> 和<filter-mapping> 元素來定義過濾器:
          <filter>
          <filter-name>LoggingFilter</filter-name>
          <filter-class>org.sample.LoggingFilter</filter-class>
          </filter>
          . . .
          <filter-mapping>
          <filter-name>LoggingFilter</filter-name>
          <url-pattern>/*</url-pattern>
          </filter-mapping>
          除了使用@WebFilter 和web.xml來定義過濾器外,你可通過編程方式使用 ServletContext.addFilter 方法來定義.要做到這一點,你需要在ServletContainerInitializer.onStartup方法或 ServletContextLis
          tener.contextInitialized方法中執行.addFilter方法將返回ServletRegistration.Dynamic, 可用它來URL模式映射,設置初始化參數,以及處理其它配置項:
          public class MyInitializer implements ServletContainerInitializer {
          public void onStartup (Set<Class<?>> clazzServletContext context) {
          FilterRegistration.Dynamic reg =
          context.addFilter("LoggingFilter","org.example.LoggingFilter");
          reg.addMappingForUrlPatterns(nullfalse"/");
          }
          }
          事件監聽器
          事件監聽器為ServletContex,HttpSession,ServletRequest對象提供了生命周期回調事件. 
          在那些對象中,這些監聽器都是實現了支持狀態變化事件通知接口的實現類.每個類都可通過
          @WebListener注解, 或在web.xml中聲明,或通過某個ServletContext.addListener方法注冊.
          這些監聽器典型例子是用于額外servlet的編程注冊對于程序員來說沒有明確的必要,或者數據庫連接的初始化或恢復需要在應用程序級恢復.
          對于每種事件類型,可以有多個監聽器類,容器在為每種事件類型調用監聽器bean時可指定順序. 
          在應用程序關閉時,會以相反的順序通知監聽器.
          Servlet 上下文監聽器會在那個上下文中監聽資源事件:
          @WebListener
          public class MyContextListener implements ServletContextListener {
          @Override
          public void contextInitialized(ServletContextEvent sce) {
          ServletContext context sce.getServletContext();
          //. . .
          }
          @Override
          public void contextDestroyed(ServletContextEvent sce) {
          //. . .
          }
          }
          ServletContextAttributeListener用于監聽上下文中屬性變化:
          public class MyServletContextAttributeListener
          implements ServletContextAttributeListener {
          @Override
          public void attributeAdded(ServletContextAttributeEvent event) {
          //. . . event.getName();
          //. . . event.getValue();
          }
          @Override
          public void attributeRemoved(ServletContextAttributeEvent event) {
          //. . .
          }
          @Override
          public void attributeReplaced(ServletContextAttributeEvent event) {
          //. . .
          }
          }
          HttpSessionListener用于在session中監聽資源事件:
          @WebListener
          public class MySessionListener implements HttpSessionListener {
          @Override
          public void sessionCreated(HttpSessionEvent hse) {
          HttpSession session hse.getSession();
          //. . .
          }
          @Override
          public void sessionDestroyed(HttpSessionEvent hse) {
          //. . .
          }
          }
          HttpSessionActivationListener 用于session鈍化或激活事件監聽:
          public class MyHttpSessionActivationListener
          implements HttpSessionActivationListener {
          @Override
          public void sessionWillPassivate(HttpSessionEvent hse) {
          // ... hse.getSession();
          }
          @Override
          public void sessionDidActivate(HttpSessionEvent hse) {
          // ...
          }
          }
          HttpSessionAttributeListener 用于監聽session中屬性變化:
          public class MyHttpSessionAttributeListener
          implements HttpSessionAttributeListener {
          @Override
          public void attributeAdded(HttpSessionBindingEvent event) {
          HttpSession session = event.getSession();
          //. . . event.getName();
          //. . . event.getValue();
          }
          @Override
          public void attributeRemoved(HttpSessionBindingEvent event) {
          //. . .
          }
          @Override
          public void attributeReplaced(HttpSessionBindingEvent event) {
          //. . .
          }
          }
          HttpSessionBindingListener用于監聽session中綁定或解綁對象的事件:
          public class MyHttpSessionBindingListener
          implements HttpSessionBindingListener {
          @Override
          public void valueBound(HttpSessionBindingEvent event) {
          HttpSession session = event.getSession();
          //. . . event.getName();
          //. . . event.getValue();
          }
          @Override
          public void valueUnbound(HttpSessionBindingEvent event) {
          //. . .
          }
          }
          ServletRequestListener 用于在request中監聽資源事件:
          @WebListener
          public class MyRequestListener implements ServletRequestListener {
          @Override
          public void requestDestroyed(ServletRequestEvent sre) {
          ServletRequest request sre.getServletRequest();
          //. . .
          }
          @Override
          public void requestInitialized(ServletRequestEvent sre) {
          //. . .
          }
          }
          ServletRequestAttributeListener用于在request中監聽屬性變化.
          還有AsyncListener,它用來管理完成,超時或錯誤的異步事件.
          除了使用@WebListener和web.xml來定義外, 你還可以使用ServletContext.addListener方法編程定義.在ServletContainerInitializer.onStartup或ServletContextListener.contextInitialized方法中可做到這一點.
          ServletContainerInitializer.onStartup方法是當程序啟動時調用的:
          public class MyInitializer implements ServletContainerInitializer {
          public void onStartup(Set<Class<?>> clazzServletContext context) {
          context.addListener("org.example.MyContextListener");
          }
          }
          異步支持
          服務器資源是保貴的,應該謹慎地使用. 考慮一個等待池中JDBC連接可用的servlet,接收JMS消息或從文件系統讀取資源的servlet.等待長時間運行進程返回會完全阻塞線程—等待, 坐著,什么事都不做—它不是一個服務器資源的最佳使用. 這里服務器可以異步處理,例如,在等待長時間運行的進程完成時,控制(或線程)將返回給容器執行其他任務. 在響應從長時間處理過程中重新返回時,請求將繼續在同一個線程中進行, 或者在長時間處理過程中,分配到新資源執行.
          長時間處理的典型使用是聊天程序.
          異步行為需要在servlet上明確地啟用.你可以在@WebServlet中添加asyncSupported屬性來做到這一點:
          @WebServlet(urlPatterns="/async"asyncSupported=true)
          public class MyAsyncServlet extends HttpServlet {
          //. . .
          }
          你也可以在web.xml中通過設置<async-supported>為ture或在程序注冊期間調用ServletRegistration.setAsyncSupported(true)來啟用異步.
          然后可在單獨線程中在request上使用startAsync方法來啟動異步處理.此方法會返回AsyncContext, 它代表的是異步請求的執行上下文.隨后,你可以調用AsyncContext.complete (explicit) 來完成異步請求或將其調度到其它資源 (隱性地). 在后一種情況下,容器將完成異步請求的調用.
          讓我們實現長時間運行處理:
          class MyAsyncService implements Runnable {
          AsyncContext ac;
          public MyAsyncService(AsyncContext ac) {
          this.ac ac;
          }
          @Override
          public void run() {
          //. . .
          ac.complete();
          }
          }
          可從goGet方法中調用此service:
          @Override
          protected void doGet(HttpServletRequest request,HttpServletResponse response) {
          AsyncContext ac request.startAsync();
          ac.addListener(new AsyncListener() {
          public void onComplete(AsyncEvent event)
          throws IOException {
          //. . .
          }
          public void onTimeout(AsyncEvent event)
          throws IOException {
          //. . .
          }
          //. . .
          });
          ScheduledThreadPoolExecutor executor new ScheduledThreadPoolExecutor(10);
          executor.execute(new MyAsyncService(ac));
          }
          在上面的代碼中,請求被放入了異步模式. AsyncListener注冊為事件監聽器,當請求處理完成時,超時時,或出現錯誤時,將會調用相關的事件方法.長時間運行的服務是在單獨線程上調用的, 調用Async
          Context.complete時就表示請求處理完成了.
          一個請求可從異步servlet調度給同步servlet,但其它方式是非法的.
          異步行為在servlet過濾中也可用.
          非阻塞 I/O
          Servlet 3.0只允許在傳統的I/O上異步請求處理,這限制了應用程序的可擴展性。在典型應用中,這通過while循環來讀取ServletInputStream:
          protected void doGet(HttpServletRequest request, HttpServletResponse response)
          throws IOException, ServletException {
          ServletInputStream input = request.getInputStream();
          byte[] b = new byte[1024];
          int len = -1;
          while ((len = input.read(b)) != -1) {
          //. . .
          }
          }
          如果到來的數據是阻塞的或流速慢于服務器讀取的速度,那么服務器線程會等待數據. 當將數據寫入
          ServletOutputStream時,也會發生同樣的情況.這限制了Web容器的可擴展性.
          非阻塞I/O允許開發者在數據可用時讀取數據或者寫數據. 這不僅增加了Web容器的可擴展性,而且增加了可以同時處理的連接數量. 非阻塞I/O只能工作Servlets,Servlets, Filters, 以及Upgrade Processing的異步處理過程中.
          Servlet 3.1通過引入兩個新接口來實現非阻塞I/O: ReadListener和WriteListener.這些監聽器的回調 方法會在內容無阻塞地可讀或可寫時調用.
          在這種情況下,需要重寫doGet方法:
          AsyncContext context = request.startAsync();
          ServletInputStream input = request.getInputStream();
          input.setReadListener(new MyReadListener(input, context));
          調用setXXXListener方法表示使用非阻塞I/O來代替傳統I/O.
          ReadListener有三個回調方法:
          • onDataAvailable回調方法會在數據可無阻塞讀取時調用.
          • onAllDataRead回調方法會在當前請求數據完全讀取后調用.
          • onError回調會在處理請求時出現錯誤時調用
          @Override
          public void onDataAvailable() {
          try {
          StringBuilder sb new StringBuilder();
          int len = -1;
          byte b[] = new byte[1024];
          while (input.isReady() && (len input.read(b)) != -1) {
          String data new String(b0len);
          }
          catch (IOException ex) {
          //. . .
          }
          }
          @Override
          public void onAllDataRead() {
          context.complete();
          }
          @Override
          public void onError(Throwable t) {
          t.printStackTrace();
          context.complete();
          }
          在上面的代碼中,onDataAvailable回調將在數據可無阻塞讀取時調用. ServletInputStream.isReady 方法用于檢查數據是否可以無阻塞地讀以及是否準備好讀. context.complete會在
          onAllDataRead 和 onError 方法中調用以發出數據讀取完成的信號. ServletInputStream.isFinished方法可用來檢查非阻塞I/O讀的狀態.
          在ServletInputStream上最多只能注冊一個ReadListener.
          WriteListener有兩個回調方法:
          • onWritePossible回調方法會在當數據可以無阻塞寫時調用.
          • onError回調會在處理響應錯誤時調用.
          ServletOutputStream上最多可注冊一個WriteListener. 
          ServletOutputStream.canWrite是用于檢查數據是否可以無阻塞寫的新方法.
          Web 片斷(Fragment)
          web 片斷是包含在library或框架(framework )JAR中META-INF目錄下的部分或全部web.xml 文件
          如果這個框架綁定在WEB-INF/lib 目錄下,容器會進行提取并配置,不需要開發明確地處理.
          它幾乎可以包含web.xml中能指定的所有元素,但其頂層元素必須是web-fragment,且相應的文件名必須是webfragment.xml. 這可用來邏輯分割web程序:
          <web-fragment>
          <filter>
          <filter-name>MyFilter</filter-name>
          <filter-class>org.example.MyFilter</filter-class>
          <init-param>
          <param-name>myInitParam</param-name>
          <param-value>...</param-value>
          </init-param>
          </filter>
          <filter-mapping>
          <filter-name>MyFilter</filter-name>
          <url-pattern>/*</url-pattern>
          </filter-mapping>
          </web-fragment>
          開發者在web.xml和web-fragment.xml中指定加載的順序.web.xml中的<absolute-ordering>元素用于指定資源應該加載的準確順序,web-fragment.xml 中的<ordering>元素用于指定相對順序. 這兩個順序是互斥的,絕對順序會覆蓋相對順序。absolute ordering可包含一個或多個<name> 元素(用于指定資源名稱和加載順序).指定<others/>允許其它資源在加載時不指定名稱:
          <web-app>
          <name>MyApp</name>
          <absolute-ordering>
          <name>MyServlet</name>
          <name>MyFilter</name>
          </absolute-ordering>
          </web-app>
          在上面的代碼中,在web.xml中指定的資源將首先加載,然后才是MyServlet和MyFilter.
          在<ordering>中的0個或一個<before> ,<after>元素 用來在webfragment 指定需要之前和之后加載的資源順序:
          <web-fragment>
          <name>MyFilter</name>
          <ordering>
          <after>MyServlet</after>
          </ordering>
          </web-fragment>
          上面的代碼會要求容器在MyServlet(其它地方定義)之后加載MyFilter.
          如果web.xml中將metadata-complete設置為true,那么web-fragment.xml 文件將不被處理
          當web.xml和web-fragment.xml配置沖突時,web.xml文件有最高優先級.
          如果web-fragment.xml 文件中沒有<ordering>元素,并且web.xml 沒有<absolute-ordering>元素,資源將假設為沒有任何依賴.
          安全
          Servlets通常都會通過Internet來訪問,因此對于安全要求是常見的.你可以通過注解或web.xml中指定servlet的安全模型包括角色,訪問控制,認證要求.
          @ServletSecurity可為servlet實現類的所有方法或特定doXXX方法指定安全約束 .這樣容器就會強制相應的doXXX 方法按用戶對應角色進行調用
          @WebServlet("/account")
          @ServletSecurity(value=@HttpConstraint(rolesAllowed = {"R1"}),httpMethodConstraints={
          @HttpMethodConstraint(value="GET",rolesAllowed="R2"),
          @HttpMethodConstraint(value="POST",rolesAllowed={"R3""R4"})
          }
          )
          public class AccountServlet
          extends javax.servlet.http.HttpServlet {
          //. . .
          }
          在上面的代碼中, @HttpMethodConstraint用于指定doGet方法可由擁有R2角色的用戶調用,doPost方法可由擁有R3和R4角色的用戶調用.@HttpConstraint指定所有其它方法可由擁有R1角色的用戶調用. 這些角色與容器中安全主體或組匹配.
          安全約束可使用web.xml中的<security-constraint>元素指定.在它這中,<web-resource-collection> 元素用于指定HTTP操作和web資源的約束, <auth-constraint> 用于指定可訪問資源的角色,<user-data-constraint> 用于表明客戶端和服務器的數據如何通過子元素<transport-guarantee>來保護:
          <security-constraint>
          <web-resource-collection>
          <url-pattern>/account/*</url-pattern>
          <http-method>GET</http-method>
          </web-resource-collection>
          <auth-constraint>
          <role-name>manager</role-name>
          </auth-constraint>
          <user-data-constraint>
          <transport-guarantee>INTEGRITY</transport-guarantee>
          </user-data-constraint>
          </security-constraint>
          此描述符只保護/account/* 的URL上的GET請求. 此方法只能由具有manager角色的用戶來訪問. 
          除了GET方法外,其它HTTP方法是不受保護的.
          如果HTTP方法沒有在安全約束中列出,約束定義的保護將會應用于整個HTTP (extension)方法:
          <security-constraint>
          <web-resource-collection>
          <url-pattern>/account/*</url-pattern>
          </web-resource-collection>
          . . .
          </security-constraint>
          在上面的代碼中,所有 /account/*方法都將保護.
          Servlet 3.1 定義了未覆蓋(uncovered )HTTP 協議方法作為未列舉在<security-constraint>中的方法,如果至少有一個<http-method>列舉在<securityconstraint>中:
          <security-constraint>
          <web-resource-collection>
          <url-pattern>/account/*</url-pattern>
          <http-method>GET</http-method>
          </web-resource-collection>
          . . .
          </security-constraint>
          在這段代碼片斷中,只有HTTP GET 方法受保護,所有其它HTTP 協議方法如POST,PUT是未覆蓋的.
          <http-method-omission>元素用于指定不受約束保護的HTTP方法列表:
          <security-constraint>
          <web-resource-collection>
          <url-pattern>/account/*</url-pattern>
          <http-method-omission>GET</http-method-omission>
          </web-resource-collection>
          . . .
          </security-constraint>
          在這段代碼中,只有HTTP GET方法不受保護,所有其它協議方法都將受保護.
          <deny-uncovered-http-methods>元素(Servlet 3.1中的新元素),可用來拒絕未覆蓋HTTP方法的HTTP方法請求. 拒絕請求是以403 (SC_FORBIDDEN)狀態碼返回的:
          <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
          http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
          version="3.1">
          <deny-uncovered-http-methods/>
          <web-resource-collection>
          <url-pattern>/account/*</url-pattern>
          <http-method>GET</http-method>
          </web-resource-collection>
          . . .
          </web-app>
          在這段代碼中,<deny-uncovered-http-methods> 元素可確保HTTP GET方法可使用需要的安全約束訪問,所有其它HTTP方法都會使用403狀態碼拒絕.
          @RolesAllowed, @DenyAll, @PermitAll, 以及 @TransportProtected 提供了另外的注解來在特定資源或資源方法上指定安全角色:
          @RolesAllowed("R2")
          protected void doGet(HttpServletRequest requestHttpServletResponse response) {
          //. . .
          }
          如果在類和方法上都指定了注解,那么方法上的注解將覆蓋類上指定的.
          Servlet 3.1 引入了兩個新的預定義角色:
          • * 匹配任何已定義角色.
          • ** 可獨立于角色匹配任何認證的用戶.
          這使得你可以站在比特定角色更高的層次上來指定安全約束.
          最多只允許@RolesAllowed, @DenyAll, 或@PermitAll其中一個指定在目標上.
          @TransportProtected 注解可以與@RolesAllowed與@PermitAll 組合使用.
          servlets可為HTTP Basic, HTTP Digest, HTTPS Client,以及基于表單認證進行配置:
          <form method="POST" action="j_security_check">
          <input type="text" name="j_username">
          <input type="password" name="j_password" autocomplete="off">
          <input type="button" value="submit">
          </form>
          這段代碼展示如何實現基于表單的認證.登錄表單必須包含輸入用戶名和密碼的字段.這些字段必須命名為j_username 和 j_password.表單的action總是j_security_check.
          Servlet 3.1 要求要密碼表單字段中使用autocomplete="off",這樣表單的安全將更強鍵.
          HttpServletRequest對于登錄,登出,以及認證方法也提供了編程安全.
          登錄方法使用ServletContext中配置的密碼驗證機制來提供的用戶名和密碼。
          這就確保了getuserprincipal,getremoteuser,和getauthtype方法返回有效值。
          可以使用登錄方法作為基于表單登錄的替換。
          驗證方法使用容器登錄機制配置ServletContext認證使用這個請求的用戶。
          資源打包
          你可以使用ServletContext.getResource和.getResourceAsStream方法來訪問綁定在.war文件中的資源.其資源路徑是以前導"/.”開頭的字符串.此路徑是通過相對于上下文的root或相對于綁定于WEB-INF/lib下JAR文件中的META-INF/resources目錄來解析的:
          myApplication.war
          WEB-INF
          lib
          library.jar
          library.jar有下面的結構:
          library.jar
          MyClass1.class
          MyClass2.class
          stylesheets
          common.css
          images
          header.png
          footer.png
          正常情況下,如果stylesheets和image 目錄要在servlet中訪問,你需要將它們手動抽取到web程序的root之下. Servlet 3.0允許將這些資源打包到META-INF/resources目錄中:
          library.jar
          MyClass1.class
          MyClass2.class
          META-INF
          resources
          stylesheets
          common.css
          images
          header.png
          footer.png
          在這種情況下,資源不需要抽取到程序的root下,相反可以直接訪問. 這樣就允許直接訪問第三方jar包中META-INF/resources,而不用手動抽取.
          應用程序會先查詢root下的資源,然后再掃描WEB-INF/lib目錄下JARs中的資源. 至于WEBINF/lib 目錄下的JAR文件的掃描順序卻是不確定的.
          錯誤處理
          HTTP 錯誤碼或servlet拋出的異常,可以通過自定義錯誤頁面來完成. 
          這些頁面是通過<error-page>來定義的:
          <error-page>
          <error-code>404</error-code>
          <location>/error-404.jsp</location>
          </error-page>
          在web.xml中加入上面的片段后,客戶端訪問不存在資源時,將顯示/error-404.jsp 頁面
          你也可以很容易地添加其它<error-page>元素來映射其它HTTP狀態碼.
          <exception-type>元素用來映射servlet拋出異常時跳轉的頁面:
          <error-page>
          <exception-type>org.example.MyException</exception-type>
          <location>/error.jsp</location>
          </error-page>
          在web.xml中增加上面的片段后,當servlet拋出org.example.MyException異常時,將會顯示/error.jsp 頁面.你可以輕松地添加其它<error-page>元素來映射其它異常.
          每個<error-page>聲明對于類名和HTTP狀態來說,必須是唯一的.
          處理Multipart Requests
          @MultipartConfig 可指定在servlet上,用于表示它希望multipart/form-data請求類型. HttpServletRequest.getParts和.getPart 方法可構造multipart request的不同部分:
          @WebServlet(urlPatterns = {"/FileUploadServlet"})
          @MultipartConfig(location="/tmp")
          public class FileUploadServlet extends HttpServlet {
          @Override
          protected void doPost(HttpServletRequest request,HttpServletResponse response)
          throws ServletExceptionIOException {
          for (Part part request.getParts()) {
          part.write("myFile");
          }
          }
          }
          在這段代碼中:
          • @MultipartConfig是在類上指定的,這就表明doPost方法可用來接收multipart/form-data類型請求.
          • location屬性用來指定文件存儲的目錄位置(根據java ee7指南,此屬性指定的絕對路徑,而且此目錄在上傳前需要提前創建)
          • getParts 方法為這個multipart request提供了part集合
          • part.write用來將上傳的part寫入磁盤.
          Servlet 3.1增加了一個新方法, Part.getSubmittedFileName,用于獲取客戶端指定的文件名稱.
          This servlet can be invoked from a JSP page:
          <form action="FileUploadServlet"
          enctype="multipart/form-data"
          method="POST">
          <input type="file" name="myFile"><br>
          <input type="Submit" value="Upload File"><br>
          </form>
          在這段代碼中,表單使用multipart/form-data通過POST方法提交到了FileUploadServlet.
          升級處理
          HTTP 1.1 (RFC 2616)的 14.42章節定義了一種允許將HTTP1.1過度到其它不兼容協議的升級機制. 
          協議更改后應用層通信的能力和性質完全依賴于所選擇的新協議. 在客戶端和服務器之間協商升級后,隨后的請求使用新選擇的消息協議交流.一個典型的例子是HTTP如何升級到WebSocket協議,在RFC 6455中開放的握手部分描述.
          servlet容器提供了一個HTTP升級機制。然而,servlet容器本身沒有任何關于升級協議知識。協議處理封裝在HttpUpgradeHandler。數據讀取或寫入servlet容器和HttpUpgradeHandler之間是字節流。
          決定升級是在servlet.service方法中決定的.
          升級可通過添加新方法實現,即 HttpServletRequest.upgrade,和 兩個新接口:javax.servlet.http.HttpUpgradeHandler 和 javax.servlet.http.WebConnection:
          if (request.getHeader("Upgrade").equals("echo")) {
          response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
          response.setHeader("Connection""Upgrade");
          response.setHeader("Upgrade""echo");
          request.upgrade(MyProtocolHandler.class);
          System.out.println("Request upgraded to MyProtocolHandler");
          }
          請求會查找 Upgrade header,根據其值來做決定.
          在這中情況下,如果Upgrade header等價于echo,連接就會升級,同時會設置響應狀態和headers
          升級方法是通過傳遞HttpUpgradeHandler實例在HttpServletRequest上調用的.
          在退出servlet的service方法后, servlet容器會完成所有過濾器的處理,并且會使連接通過HttpUpgradeHandler實例處理:
          public class MyProtocolHandler implements HttpUpgradeHandler {
          @Override
          public void init(WebConnection wc) {
          //. . .
          }
          @Override
          public void destroy() {
          //. . .
          }
          }
          這段代碼展示了一個HttpUpgradeHandler的實現. servlet容器會調用HttpUpgradeHandler的初始化方法, 傳遞WebConnection以允許協議處理器可訪問數據流.
          當升級處理過程完成時,會調用其HttpUpgradeHandler.destroy方法.
          servlet過濾器只處理初始的HTTP請求和響應,在后續通信中,將不再被調用.
          posted on 2016-07-24 01:32 胡小軍 閱讀(866) 評論(0)  編輯  收藏 所屬分類: Java EE7

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 福泉市| 华安县| 宾川县| 亚东县| 安西县| 沂水县| 丰原市| 澜沧| 秦安县| 邳州市| 泾源县| 阳城县| 紫阳县| 锡林浩特市| 专栏| 屯留县| 兴安县| 潍坊市| 秭归县| 吴川市| 文昌市| 大冶市| 新乡县| 上高县| 巴彦县| 福海县| 大埔县| 新余市| 鄂尔多斯市| 上蔡县| 车致| 阳朔县| 泽普县| 安乡县| 柏乡县| 诸城市| 华容县| 平远县| 于田县| 鄂托克前旗| 原阳县|