筆記

          way

              數組可以裝基本類型或者引用,collections只能裝引用。
              通常有兩種方法可以擴展collection 來滿足一些需要:繼承某種集合類型和封裝某種集合類型。第一種的優點是初始化的時候在內存中只產生一個對象,這是繼承特性決定的。后者的優點是我們可以方便控制被封裝集合的各種屬性。
          Whenever possible, it’s desirable to bury implementation details inside of a class rather than exposing client code to such details。例:
          法1:
          public class Student {
              private String name;
              private String studentId;  
              private ArrayList<TranscriptEntry> transcript; //成績報告單
              public void addTranscriptEntry(TranscriptEntry te) {   // 操作transcript達到記錄成績
                  // Store the TranscriptEntry in our ArrayList.
                  transcript.add(te);
              }
          }
          客戶端調用代碼:
          Student s = new Student("1234567", "James Huddleston");
          Course c = new Course("LANG 800", "Advanced Language Studies");
          TranscriptEntry te = new TranscriptEntry(c, "Fall 2006", "B+");
          s.addTranscriptEntry(te);
          法2:
          建立新對象,封裝一個ArrayList:
          public class Transcript {
              private ArrayList<TranscriptEntry> transcriptEntries;
              public void courseCompleted(Course c, String semester, String grade) {
                  // Instantiate and insert a brand-new TranscriptEntry object into the
                  // ArrayList - details hidden away!
                  transcriptEntries.add(new TranscriptEntry(c, semester, grade);
              }
          }
          public class Student {
              private String name;
              private String studentId;
              // This used to be declared as an ArrayList.
              private Transcript transcript;
              // etc.
          }
          客戶端代碼:
          s.courseCompleted(c, "Spring 2006", "A+");
          第二種方法使Student處理更少的細節,不用管transcripts怎么表達,看不到TranscriptEntry的存在。客戶端代碼更簡單。
          posted @ 2009-12-10 11:38 yuxh 閱讀(176) | 評論 (0)編輯 收藏
          Aggregation is a special form of association, alternatively referred to as the “consists of”, “is composed of”, or “has a” relationship.Like an association, an aggregation is used to represent a relationship between two classes, A and B. But, with an aggregation, we’re representing more than mere relationship: we’re stating that an object belonging to class A, known as an aggregate,is composed of, or contains, component objects belonging to class B.
            Note that these aggregation statements appear very similar to associations, where the name of the association just so happens to be is composed of or contains. That’s because an aggregation is an association in the broad sense of the term(aggregation 是association的一種特殊表現形式)!aggregation 和associations UML表現不同但最終代碼表現形式一樣
            Composition is a strong form of aggregation, in which the “parts” cannot exist without the “whole.” As an example, given the relationship “a Book is composed of many Chapters”, we could argue that a chapter cannot exist if the book to which it belongs ceases to exist; whereas given the relationship “a Car is composed of many Wheels”, we know that a wheel can be removed from a car and still serve a useful purpose. Thus, we’d categorize the Book–Chapter relationship as composition and the Car–Wheel relationship as aggregation.
          繼承沒留意的好處:
          Best of all, we can derive a new class from an existing class even if we don’t own the source code for the latter! As long as we have the compiled bytecode version of a class, the inheritance mechanism works just fine; we don’t need the original source code of a class in order to extend it. This is one of the most dramatic ways to achieve productivity with an objectoriented language: find a class (either one written by someone else or one that is built into the language) that does much of what you need, and create a subclass of that class,adding just those features that you need for your own purposes.
          classification is the natural way that humans organize information; so, it only makes sense that we’d organize software along the same lines, making it much more intuitive and hence easier to develop, maintain,extend, and communicate with users about.
          繼承與Association, aggregation異同(P186):
          Association, aggregation, and inheritance are all said to be relationships between classes. Where inheritance differs from association and aggregation is at the object level.inheritance is indeed a relationship between classes, but not between distinct objects.
          注意:避免連鎖反應,Whenever possible, avoid adding features to non-leaf classes once they have been established in code form in an application, to avoid ripple effects throughout an inheritance hierarchy. 說比做容易,這就要求在編碼之前盡可多的花時間在需求分析和對象建模階段,雖然不能避免新需求出現,但至少避免忽視遺漏了當前的需求。
               Overriding:子類繼承父類,重寫唯一能改變的是方法的訪問控制,而且只能比父類更寬松,如父類用的是private,子類可以用public。參考了下thinking in java 4th P202 發現這種說法不對,而且是一個陷阱:父類的該方法根本對子類不可見!子類的該方法實際上是一個全新的方法,連重載都算不上。所以只能重寫non-private方法。遇到private方法你得小心,沒有編譯錯誤,但不會像你想象的工作,最好給方法重新取名,避免陷阱。

          不要做的事情:
          We shouldn’t change the semantics—that is, the intention, or meaning—of a feature.For example:
          • If the print method of a superclass such as Student is intended to display the values of all of an object’s attributes in the command window, then the print method of a subclass such as GraduateStudent shouldn’t, for example, be overridden so that it directs all of its output to a file instead.
          • If the name attribute of a superclass such as Person is intended to store a person’s name in “last name, first name” order, then the name attribute of a subclass such as Student should be used in the same fashion.
            We can’t physically eliminate features, nor should we effectively eliminate them by ignoring them. To attempt to do so would break the spirit of the “is a” hierarchy. By definition, inheritance requires that all features of all ancestors of a class A must also apply to class A itself in order for A to truly be a proper subclass. If a GraduateStudent could eliminate the degreeSought attribute that it inherits from Student, for example, is a GraduateStudent really a Student after all? Strictly speaking, the answer is no.
            進一步從實用角度說,如果我們重寫一個方法但不在這方法里做任何事情,其他繼承我們類的人就會出問題:他們覺得我們的方法是有意義的(特別是他們不能看到我們源代碼的時候)。而我們則打破了“is a” 原則,所以絕不要這樣做!
                protected關鍵字的運用,用于控制繼承的訪問控制。
               運用super(arguments) 減少子類構造函數重復父類構造函數代碼,和this類似必須在構造函數最開始調用。
          Student s = new Student("Fred", "123-45-6789"); 執行這段代碼,Object的構造函數會自動執行,接著Student 的父類Person構造函數仔細,然后是我們調用的Student構造函數,如果調用的Student構造函數沒有顯示調用父類構造函數,則相當于默認調用super() 。
          java沒有類的多繼承,多繼承很復雜的一點,如果兩個父類都有相同的方法,子類怎么處理?
          posted @ 2009-12-08 16:57 yuxh 閱讀(252) | 評論 (0)編輯 收藏

          We may wish to instantiate additional objects related to the Student object:
          初始化與對象相關的一些額外對象:
          public class Student() {
              // Every Student maintains a handle on his/her own individual Transcript object.
              private Transcript transcript;
              public Student() {
                  // Create a new Transcript object for this new Student.
                  transcript = new Transcript();
                  // etc.
              }
          }
          讀取數據庫來初始化對象屬性:
          public class Student {
              // Attributes.
              String studentId;
              String name;
              double gpa;
              // etc.
              // Constructor.
              public Student(String id) {
                  studentId = id;
                  // Pseudocode.
                  use studentId as a primary key to retrieve data from the Student table of a
                  relational database;
                  if (studentId found in Student table) {
                     retrieve all data in the Student record;
                      name = name retrieved from database;
                      gpa = value retrieved from database;
                  // etc.
                  }
              }
          // etc.
          }

          和其他已存在的對象交流:
          public class Student {
              // Details omitted.
              // Constructor.
              public Student(String major) {
                  // Alert the student's designated major department that a new student has
                  // joined the university.
                  // Pseudocode.
                  majorDept.notify(about this student ...);
              // etc.
              }
              // etc.
          }

          好習慣:如果需要有參數的構造函數,最好同時顯示聲明一個無參構造函數。
          容易出現的bug:如果給構造函數加上void編譯會通過!不過會被當作方法而不是構造函數!
          當有多個構造函數,而且都有共同的初始化內容時,就會出現很多重復的代碼,比如構造一個新學生,我們會做:
          1 通知登記辦公室學生的存在
          2 給學生創建學生成績報告單
          重復引起以后修改必須修改多處,如果使用this 會得到改善

          public class Student {
           // Attribute details omitted.
           // Constructor #1.
           public Student() {
          // Assign default values to selected attributes ... details omitted.
          // Do the things common to all three constructors in this first
          // constructor ...
          // Pseudocode.
          alert the registrar's office of this student's existence
          // Create a transcript for this student.
          transcript = new Transcript();
          }

           // Constructor #2.
           public Student(String s) {
            // ... then, REUSE the code of the first constructor within the second!
            this();
            // Then, do whatever else extra is necessary for constructor #2.
            this.setSsn(s);
           }

           // Constructor #3.
           public Student(String s, String n, int i) {
            // ... and REUSE the code of the first constructor within the third!
            this();
            // Then, do whatever else extra is necessary for constructor #3.
            this.setSsn(s);
            this.setName(n);
            this.setAge(i);
           }
           // etc.
          }

          注意:this必須在方法最前面調用
          posted @ 2009-12-06 22:19 yuxh 閱讀(230) | 評論 (0)編輯 收藏

          應用一:解決tomcat下中文亂碼問題(先來個簡單的) 

          tomcat下,我們通常這樣來解決中文亂碼問題:

          過濾器代碼:

          1. package filter;  
          2.   
          3. import java.io.*;  
          4. import javax.servlet.*;  
          5. import javax.servlet.http.*;  
          6. import wrapper.GetHttpServletRequestWrapper;  
          7.   
          8. public class ContentTypeFilter implements Filter {  
          9.   
          10.     private String charset = "UTF-8";  
          11.     private FilterConfig config;  
          12.       
          13.     public void destroy() {  
          14.         System.out.println(config.getFilterName()+"被銷毀");  
          15.         charset = null;  
          16.         config = null;  
          17.     }  
          18.   
          19.     public void doFilter(ServletRequest request, ServletResponse response,  
          20.             FilterChain chain) throws IOException, ServletException {  
          21.         //設置請求響應字符編碼  
          22.         request.setCharacterEncoding(charset);  
          23.         response.setCharacterEncoding(charset);  
          24.           
          25.         HttpServletRequest req = (HttpServletRequest)request;  
          26.           
          27.           
          28.         System.out.println("----請求被"+config.getFilterName()+"過濾");  
          29.         //執行下一個過濾器(如果有的話,否則執行目標servlet)  
          30.         chain.doFilter(req, response);  
          31.           
          32.         System.out.println("----響應被"+config.getFilterName()+"過濾");  
          33.   
          34.     }  
          35.   
          36.     public void init(FilterConfig config) throws ServletException {  
          37.             this.config = config;  
          38.             String charset = config.getServletContext().getInitParameter("charset");    
          39.             if( charset != null && charset.trim().length() != 0)  
          40.             {  
          41.                 this.charset = charset;  
          42.             }  
          43.     }  
          44.   
          45. }  

          web.xml中過濾器配置
          <!--將采用的字符編碼配置成應用初始化參數而不是過濾器私有的初始化參數是因為在JSP和其他地方也可能需要使用-->
           <context-param>
             <param-name>charset</param-name>
             <param-value>UTF-8</param-value>
           </context-param>

           <filter>
            <filter-name>ContentTypeFilter</filter-name>
            <filter-class>filter.ContentTypeFilter</filter-class>
           </filter>

           <filter-mapping>
            <filter-name>ContentTypeFilter</filter-name>
            <url-pattern>/*</url-pattern>
           </filter-mapping>

          equest.setCharacterEncoding(charset); 必須寫在第一次使用request.getParameter()之前,這樣才能保證參數是按照已經設置的字符編碼來獲取。
          response.setCharacterEncoding(charset);必須寫在PrintWriter out = request.getWriter()之前,這樣才能保證out按照已經設置的字符編碼來進行字符輸出。

          通過過濾器,我們可以保證在Servlet或JSP執行之前就設置好了請求和響應的字符編碼。

          但是這樣并不能完全解決中文亂碼問題:

          對于post請求,無論是“獲取參數環節”還是“輸出環節"都是沒問題的;

          對于get請求,"輸出環節"沒有問題,但是"獲取參數環節"依然出現中文亂碼,所以在輸出時直接將亂碼輸出了。

           

          原因是post請求和get請求存放參數位置是不同的:

          post方式參數存放在請求數據包的消息體中。 get方式參數存放在請求數據包的請求行的URI字段中,以?開始以param=value&parame2=value2的形式附加在URI字段之后。而request.setCharacterEncoding(charset); 只對消息體中的數據起作用,對于URI字段中的參數不起作用,我們通常通過下面的代碼來完成編碼轉換:

           String paramValue = request.getParameter("paramName");
          paramValue = new String(paramValue.trim().getBytes("ISO-8859-1"), charset);

          但是每次進行這樣的轉換實在是很麻煩,有沒有統一的解決方案呢?

          解決方案1: 在tomcat_home"conf"server.xml 中的Connector元素中設置URIEncoding屬性為合適的字符編碼

              <Connector port="8080" protocol="HTTP/1.1"
                         connectionTimeout="20000"
                         redirectPort="8443"
                         URIEncoding="UTF-8"
               />

          這樣做的缺點是,同一個tomcat下的其他應用也將受到影響。而其每次部署時都需要類修改配置也很麻煩。

          解決方案2:自定義請求包裝器包裝請求,將字符編碼轉換的工作添加到getParameter()方法中

           

          package wrapper;

          import java.io.UnsupportedEncodingException;
          import java.net.URLDecoder;

          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletRequestWrapper;

          public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper {

              
          private String charset = "UTF-8";

              
          public GetHttpServletRequestWrapper(HttpServletRequest request) {
                  
          super(request);
              }


              
          /**
               * 獲得被裝飾對象的引用和采用的字符編碼
               * 
               * 
          @param request
               * 
          @param charset
               
          */

              
          public GetHttpServletRequestWrapper(HttpServletRequest request,
                      String charset) 
          {
                  
          super(request);
                  
          this.charset = charset;
              }


              
          /**
               * 實際上就是調用被包裝的請求對象的getParameter方法獲得參數,然后再進行編碼轉換
               
          */

              
          public String getParameter(String name) {
                  String value 
          = super.getParameter(name);
                  value 
          = value == null ? null : convert(value);
                  
          return value;
              }


              
          public String convert(String target) {
                  System.out.println(
          "編碼轉換之前:" + target);
                  
          try {
                      
          return new String(target.trim().getBytes("ISO-8859-1"), charset);
                  }
           catch (UnsupportedEncodingException e) {
                      
          return target;
                  }

              }

          }


          修改過濾器的doFilter方法 代碼如下:

              public void doFilter(ServletRequest request, ServletResponse response,
                      FilterChain chain) 
          throws IOException, ServletException {
                  
          // 設置請求響應字符編碼
                  request.setCharacterEncoding(charset);
                  response.setCharacterEncoding(charset);
                  
          // 新增加的代碼
                  HttpServletRequest req = (HttpServletRequest) request;

                  
          if (req.getMethod().equalsIgnoreCase("get")) {
                      req 
          = new GetHttpServletRequestWrapper(req, charset);
                  }


                  System.out.println(
          "----請求被" + config.getFilterName() + "過濾");
                  
          // 傳遞給目標servlet或jsp的實際上時包裝器對象的引用,而不是原始的HttpServletRequest對象
                  chain.doFilter(req, response);

                  System.out.println(
          "----響應被" + config.getFilterName() + "過濾");
              }

           

           

          這樣一來,在servlet中調用包裝器的getParameters方法來獲取參數,就已經完成了字符編碼的轉換過程,我們就不需要在每次獲取參數時來進行字符編碼轉換了。
          總結:自己寫類繼承HttpServletRequestWrapper,HttpServletRequestWrapper實現了HttpServletRequest接口。看tomcat的源代碼可以發現,ServletRequest作為一個Component ,ServletRequestWrapper作為一個比較標準的Decorator ,實現ServletRequest接口并把ServletRequest當作成員變量,其他繼承Decorator 的類(比如本例中的GetHttpServletRequestWrapper )就可以很好的操控ServletRequest及其子類(比如本例中的HttpServletRequest),HttpServletRequest的很多方法就可以根據我們的需求做改變,比如設置字符,去掉空格。
          參考:
          http://www.javaeye.com/topic/483158
          http://fishhappy365.javaeye.com/blog/484185
          http://www.javaeye.com/topic/220230
          posted @ 2009-11-22 22:30 yuxh 閱讀(7206) | 評論 (0)編輯 收藏
          1. HTTP 定義了與服務器交互的不同方法,最基本的方法是 GET 和 POST。事實上 GET 適用于多數請求,而保留 POST 僅用于更新站點。根據 HTTP 規范,GET 用于信息獲取,而且應該是 安全的和 冪等的。所謂安全的意味著該操作用于獲取信息而非修改信息。換句話說,GET 請求一般不應產生副作用。冪等的意味著對同一 URL 的多個請求應該返回同樣的結果。完整的定義并不像看起來那樣嚴格。從根本上講,其目標是當用戶打開一個鏈接時,她可以確信從自身的角度來看沒有改變資源。比如,新聞站點的頭版不斷更新。雖然第二次請求會返回不同的一批新聞,該操作仍然被認為是安全的和冪等的,因為它總是返回當前的新聞。反之亦然。POST 請求就不那么輕松了。POST 表示可能改變服務器上的資源的請求。仍然以新聞站點為例,讀者對文章的注解應該通過 POST 請求實現,因為在注解提交之后站點已經不同了(比方說文章下面出現一條注解);
          2. 在FORM提交的時候,如果不指定Method,則默認為GET請求,Form中提交的數據將會附加在url之后,以?分開與url分開。字母數字字符原樣發送,但空格轉換為“+“號,其它符號轉換為%XX,其中XX為該符號以16進制表示的ASCII(或ISO Latin-1)值。GET請求請提交的數據放置在HTTP請求協議頭中,而POST提交的數據則放在實體數據中;
          3. GET方式提交的數據最多只能有1024字節,而POST則沒有此限制。

          Web 上最常用的兩種 Http 請求就是 Get 請求和 Post 請求了。我們在做 java web 開發時,也總會在 servlet 中通過 doGet 和 doPost 方法來處理請求;更經常地,我們會在 doGet 方法的實現中調用 doPost 方法。盡管做了近兩年的 web 開發,我對諸如 Get 請求和 Post 請求的基本概念仍不是十分了解。近日閱讀《 javascript 高級程序設計》重新整理了一下 Get 請求和 Post 請求的概念,算是讀書筆記吧。

          Get 是從服務器上獲取數據,這是最常見的請求類型。每次在瀏覽器中輸入 URL 打開頁面時,就是向服務器發送一個 Get 請求。 Get 請求的參數是用問號追加到 URL 結尾,后面跟著用&連接起來的名稱/值對。比如網址 http://bt.neupioneer.com/viewthread.php?tid=87813 ,其中 tid 為參數名, 87813 為參數的值。在編程中,使用 Get 最多的地方就是超鏈接列表,其中的參數多是從數據庫讀出的字段拼接而成。在 Ajax 中,我們也經常使用 Get ,通過提取出頁面的標簽值,拼成串后構造一個 URL 。 Get 在使用上是有限制的, URL 的最大長度為 2KB ,因此,如果表單中包含textarea這樣的大文本段,就不要用Get了。對于表單來說, Get 是把參數數據隊列加到提交表單的 ACTION 屬性所指的 URL 中,值和表單內各個字段一一對應,通過 URL 可以看到中傳遞的參數。因此,相比于 Post ,它是不安全的。

          Post 的使用場合多是在表單提交的地方,因為和 Get 相比, Post 可以發送更多的數據,《 javascript 高級程序設計》中說最多可以發送 2GB ,這多少讓我不太相信,網上一些文章說 IIS4 中最大量為 80KB , IIS5 中為 100KB ,不知道 Tomcat 中的情況如何。 Post 是通過 HTTP Post 機制,將表單內各個字段與其內容放置在 HTML Header 內一起傳送到 ACTION 屬性所指的 URL 地址。 和 Get 相比, Post 的內容是不會在 URL 中顯現出來的,這多少是安全一些的。 我在做登錄這樣的表單時,只是將請求方式設為 Post ,使得用戶名和密碼信息不在瀏覽器中顯現,但不清楚的是,是否有更好的方法加密密碼等信息(實在不知道如果請求不傳到服務器的話,怎么對未知的請求加密,清楚的朋友不妨給個解決方案)。在 Ajax 中,如果要和服務器交互,記得加上 request.setRequestHeader(“Content-Type”,”application/x-www-urlencoded”); 這一腳本,盡管很多 Ajax 教材中都提到了這一點。另外要說的是,被傳遞的參數是要經過編碼的。在 javascript 中,編碼函數是 encodeURIComponent(xx) 。  
              以上轉自 http://blog.csdn.net/yoland/archive/2009/03/19/4005740.aspx
          一個get的發送請求例子:
          GET /select/selectBeerTaste.jsp?color=dark&taste=malty HTTP/1.1
          Host: www.wickedlysmart.com
          User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.4) Gecko/
          20030624 Netscape/7.1
          Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/
          plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
          Accept-Language: en-us,en;q=0.5
          Accept-Encoding: gzip,deflate
          Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
          Keep-Alive: 300
          Connection: keep-alive

          一個post的發送請求例子:
          POST /advisor/selectBeerTaste.do HTTP/1.1
          Host: www.wickedlysmart.com
          User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.4) Gecko/
          20030624 Netscape/7.1
          Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/
          plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
          Accept-Language: en-us,en;q=0.5
          Accept-Encoding: gzip,deflate
          Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
          Keep-Alive: 300
          Connection: keep-alive
          color=dark&taste=malty (Request headers 后面有一個傳參數的body)
          兩個例子的紅色的部分很好的看出了get和post傳參數區別
          posted @ 2009-11-22 21:12 yuxh 閱讀(1726) | 評論 (0)編輯 收藏

          上面是CAS 基礎協議圖 

          CAS 介紹

          CAS 具有以下特點:

          • 開源的企業級單點登錄解決方案。
          • CAS Server 為需要獨立部署的 Web 應用。
          • CAS Client 支持非常多的客戶端(這里指單點登錄系統中的各個 Web 應用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。

          CAS 原理和協議

          從結構上看,CAS 包含兩個部分: CAS Server 和 CAS Client。CAS Server 需要獨立部署,主要負責對用戶的認證工作;CAS Client 負責處理對客戶端受保護資源的訪問請求,需要登錄時,重定向到 CAS Server。

          CAS Client 與受保護的客戶端應用部署在一起,以 Filter 方式保護受保護的資源。對于訪問受保護資源的每個 Web 請求,CAS Client 會分析該請求的 Http 請求中是否包含 Service Ticket,如果沒有,則說明當前用戶尚未登錄,于是將請求重定向到指定好的 CAS Server 登錄地址,并傳遞 Service (也就是要訪問的目的資源地址),以便登錄成功過后轉回該地址。用戶在第 3 步中輸入認證信息,如果登錄成功,CAS Server 隨機產生一個相當長度、唯一、不可偽造的 Service Ticket,并緩存以待將來驗證,之后系統自動重定向到 Service 所在地址,并為客戶端瀏覽器設置一個 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新產生的 Ticket 過后,在第 5,6 步中與 CAS Server 進行身份合適,以確保 Service Ticket 的合法性。

          在該協議中,所有與 CAS 的交互均采用 SSL 協議,確保,ST 和 TGC 的安全性。協議工作過程中會有 2 次重定向的過程,但是 CAS Client 與 CAS Server 之間進行 Ticket 驗證的過程對于用戶是透明的。

          另外,CAS 協議中還提供了 Proxy (代理)模式,以適應更加高級、復雜的應用場景,具體介紹可以參考 CAS 官方網站上的相關文檔。

          在 Tomcat 上部署一個完整的 CAS Server 主要按照以下幾個步驟

          配置 Tomcat 使用 Https 協議

          如果希望 Tomcat 支持 Https,主要的工作是配置 SSL 協議,其配置過程和配置方法可以參考 Tomcat 的相關文檔。不過在生成證書的過程中,會有需要用到主機名的地方,CAS 建議不要使用 IP 地址,而要使用機器名或域名。(項目過程中沒使用)

          部署 CAS Server

          CAS Server 是一個 Web 應用包,將前面下載的 cas-server-3.1.1-release.zip 解開,把其中的 cas-server-webapp-3.1.1.war 拷貝到 tomcat的 webapps 目錄,并更名為 cas.war。由于前面已配置好 tomcat 的 https 協議,可以重新啟動 tomcat,然后訪問:https://localhost:8443/cas(沒配置ssh應該是http://localhost:8080/cas) ,如果能出現正常的 CAS 登錄頁面,則說明 CAS Server 已經部署成功。

          雖然 CAS Server 已經部署成功,但這只是一個缺省的實現,在實際使用的時候,還需要根據實際概況做擴展和定制,最主要的是擴展認證 (Authentication) 接口和 CAS Server 的界面。

          參考http://www.ibm.com/developerworks/cn/opensource/os-cn-cas/index.html
          項目也采用jdbc驗證,文章介紹了三個基于 JDBC 的 AuthenticationHandler,我使用的cas-server 3.3 多了一個AbstractJdbcUsernamePasswordAuthenticationHandler 直接使用。

          部署客戶端應用

          單點登錄的目的是為了讓多個相關聯的應用使用相同的登錄過程,使用的是cas-client-core- 3.1.9.jar 放入要使用單點登錄項目的lib下。cas客戶端使用的是過濾器,在web.xml配置。可以使用spring配置bean。官方網站http://www.ja-sig.org/wiki/display/CASC/Configuring+the+JA- SIG+CAS+Client+for+Java+using+Spring 有配置。
          用類擴展 Cas20ProxyReceivingTicketValidationFilter復寫onSuccessfulValidation方法,寫入驗證成功后做的事情,比如把用戶信息放入session。寫了一個a.jsp用于跳轉,過濾器針對他,訪問這個頁面的時候,會到cas服務器端,如果成功的話,返回到a.jsp,這個頁面用sendRedirect調用了一個action方法處理登錄成功后直接進入項目首頁。

          posted @ 2009-11-12 20:57 yuxh 閱讀(1267) | 評論 (0)編輯 收藏

          1 . SSL(Server Socket Layer) 簡介

          在網絡上信息在源 - 宿的傳遞過程中會經過其它的計算機。一般情況下,中間的計算機不會監聽路過的信息。但在使用網上銀行或者進行信用卡交易的時候有可能被監視,從而導致個人 隱私的泄露。由于 Internet 和 Intranet 體系結構的原因,總有某些人能夠讀取并替換用戶發出的信息。隨著網上支付的不斷發展,人們對信息安全的要求越來越高。因此 Netscape 公司提出了 SSL 協議,旨在達到在開放網絡 (Internet) 上安全保密地傳輸信息的目的,這種協議在 WEB 上獲得了廣泛的應用。 之后 IETF(ietf.org) 對 SSL 作了標準化,即 RFC2246 ,并將其稱為 TLS ( Transport Layer Security ),從技術上講, TLS1.0 與 SSL3.0 的差別非常微小。

          2 . SSL 工作原理

          SSL 協議使用不對稱加密技術實現會話雙方之間信息的安全傳遞。可以實現信息傳遞的保密性、完整性,并且會話雙方能鑒別對方身份。不同于常用的 http 協議,我們在與網站建立 SSL 安全連接時使用 https 協議,即采用 https://ip:port/ 的方式來訪問。當我們與一個網站建立 https 連接時,我們的瀏覽器與 Web Server 之間要經過一個握手的過程來完成身份鑒定與密鑰交換,從而建立安全連接。具體過程如下:

          用戶瀏覽器將其 SSL 版本號、加密設置參數、與 session 有關的數據以及其它一些必要信息發送到服務器。

          服務器將其 SSL 版本號、加密設置參數、與 session 有關的數據以及其它一些必要信息發送給瀏覽器,同時發給瀏覽器的還有服務器的證書。如果配置服務器的 SSL 需要驗證用戶身份,還要發出請求要求瀏覽器提供用戶證書。

          客戶端檢查服務器證書,如果檢查失敗,提示不能建立 SSL 連接。如果成功,那么繼續。客戶端瀏覽器為本次會話生成 pre-master secret ,并將其用服務器公鑰加密后發送給服務器。如果服務器要求鑒別客戶身份,客戶端還要再對另外一些數據簽名后并將其與客戶端證書一起發送給服務器。

          如果服務器要求鑒別客戶身份,則檢查簽署客戶證書的 CA 是否可信。如果不在信任列表中,結束本次會話。如果檢查通過,服務器用自己的私鑰解密收到的 pre-master secret ,并用它通過某些算法生成本次會話的 master secret 。

          客戶端與服務器均使用此 master secret 生成本次會話的會話密鑰 ( 對稱密鑰 ) 。在雙方 SSL 握手結束后傳遞任何消息均使用此會話密鑰。這樣做的主要原因是對稱加密比非對稱加密的運算量低一個數量級以上,能夠顯著提高雙方會話時的運算速度。

          客戶端通知服務器此后發送的消息都使用這個會話密鑰進行加密。并通知服務器客戶端已經完成本次 SSL 握手。

          服務器通知客戶端此后發送的消息都使用這個會話密鑰進行加密。并通知客戶端服務器已經完成本次 SSL 握手。

          本次握手過程結束,會話已經建立。雙方使用同一個會話密鑰分別對發送以及接受的信息進行加、解密。
          posted @ 2009-11-12 17:38 yuxh 閱讀(176) | 評論 (0)編輯 收藏
              為了支持請求代理,Servlet API 2.1 引入javax.servlet.RequestDispatcher接口。servlet調用請求對象的getRequestDispatcher()的方法獲得RequestDispatcher實例,此實例可以dispatch到指定的路徑上的組件(如servlet, JSP,靜態文件等):
              public RequestDispatcher ServletRequest.getRequestDispatcher(String path)
          ,可以是相對路徑但不能超過當前的servlet context,不過可以使用getContext方法轉發到當前context之外的地方,只是沒有任何方法轉發到另一個服務器上的context。如果路徑以“/”開始,它被解釋為相對于當前context根,如果路徑包含查詢字符,參數會被包含到接受組件的參數集的開始處。如果servlet容器由于任何原因不能返回RequestDispatcher 該方法返回null。在ServletContext類中有同名方法
          public RequestDispatcher ServletContext.getRequestDispatcher(String path),不同之處在于ServletContext 中的方法(sevlet2.1引入)只接受絕對URLs(以/開頭),而ServletRequest 中的方法接受絕對和相對。因此沒有理由使用ServletContext中的該方法,可以看作只為歷史版本兼容(不過servlet2.5仍然有此方法)。除了用路徑還可以用名字指定資源來獲得RequestDispatcher,ServletContext中的方法:
              public RequestDispatcher ServletContext.getNamedDispatcher(String name),此方法允許轉發到沒必要公開發布的資源,servlet或jsp可能通過web應用描述符獲得名字。不能獲取RequestDispatcher仍然返回null。
              RequestDispatcher有兩個方法:forward()and include()。forward把整個請求交給代理;include把代理的輸出加入調用servlet(個人理解為初始的那個servlet)的response中并且調用servlet仍然處于控制狀態,即請求轉發后,原先的Servlet還可以繼續輸出響應信息,轉發到的Servlet對請求做出的響應將并入原先Servlet的響應對象中
              forward把請求從一個servlet轉發到服務器上的另一個資源,它允許一個servlet對請求做初始處理,然后另一個資源來產生response,不像sendRedirect,forward的整個操作都在服務器內部,對客戶是透明的。附加查詢字符串或使用請求的setAttribute的方法可以傳遞信息給代理

          The rules a forwarding servlet must follow are relatively strict:

          • It may set headers and the status code but may not send any response body to the client (that's the job for an include). Consequently, the forward( ) must be called before the response has been committed.

          • If the response already has been committed, the forward( ) call throws an IllegalStateException.

          • If the response has not been committed but there's content within the response buffer, the buffer is automatically cleared as part of the forward.

          • In addition, you can't get creative by substituting new request and response objects. The forward( ) method must be called with the same request and response objects as were passed to the calling servlet's service method, and the forward( ) must be called from within the same handler thread.

          如果servlet A轉發到servlet B,B得到的路徑信息應該是和直接調用B一樣的,從這方面來說B完全控制請求。這也是為什么確保response的緩存會在調用之前沖掉,response沒有被提交是很重要的事情。
              用路徑轉發的問題是:目標組件不但對服務器組件可見,也必須對客戶端可見。出于安全考慮,可能會讓一些組件不是公共可見的,所以用名字代替路徑做轉發:getNamedDispatcher 不過沒有URI路徑也就不能加上查詢字符串,路徑也不能做調整。
              forwardsendRedirect誰更好:request dispatch 在服務器端發生,redirect在客戶端發生。forward最好用在一個組件必須處理業務邏輯而且和另一個組件分享結果的情況。sendRedirict最好用于客戶需要從一頁重定向于另一頁。看起來用forward比較有誘惑,因為forward在服務器內部完成,sendRedirect還需要從客戶端倒一遍,所以forward更快。不幸的是處理相對URL的時候會引起問題。例:
          public class HomePageForward extends HttpServlet {

            public void doGet(HttpServletRequest req, HttpServletResponse res)
                                         throws ServletException, IOException {
              RequestDispatcher dispatcher = req.getRequestDispatcher("/index.html");
              dispatcher.forward(req, res);
            }
          }

          如果index.html中有<img src="../../../images/trans.gif"/>則圖片不能讀取。sendRedirect會告訴客戶端文件來源于文件根目錄,forward不會。因為不需要調用getContext查詢,sendRedirect更容易轉發到其他context的資源中。所以建議是盡量用sendRedirect,必須的情況下才用forward。 
              RequestDispatcher的include方法把資源的內容放入當前的respongse中。它實現了一種我們可以稱為編程式服務器端 include。不同于forward,在調用include后的servlet A仍然控制response而且可能包含 被包含進來內容的前后內容。
          例1:
                  doGet(..){
                      res.setContentType("text/html");
                      PrintWriter out = res.getWriter();

                      out.println("<HTML><HEAD><TITLE>Welcome to Nile</TITLE></HEAD>");
                      out.println("<BODY>");

                      // Show an item in an online catalog
                      out.println("Feast your eyes on this beauty:");

                      RequestDispatcher dispatcher =
                       req.getRequestDispatcher("/servlet/NileItem?item=0596000405");
                      dispatcher.include(req, res);

                      out.println("And, since I like you, it's 20% off!");   
                      out.println("</BODY></HTML>");
              }

          使用forward,通過附加查詢字符或setAttribute方法,信息可以傳到被調用資源。Using attributes instead of parameters gives you the ability to pass objects instead of simple strings,例
              doGet(..){
                      res.setContentType("text/html");
              PrintWriter out = res.getWriter();

              out.println("<HTML><HEAD><TITLE>Welcome to Nile</TITLE></HEAD>");
              out.println("<BODY>");

              // Show items in an online catalog
              RequestDispatcher dispatcher =
                req.getRequestDispatcher("/servlet/NileItem");

              out.println("Feast your eyes on this beauty:");
              req.setAttribute("item", Book.getBook("0596000405"));
              dispatcher.include(req, res);

              // Remove the "item" attribute after use
              req.removeAttribute("item");

              out.println("Or how about this one:");
              req.setAttribute("item", Book.getBook("0395282659"));
              dispatcher.include(req, res);

              out.println("And, since I like you, they're all 20% off!");

              out.println("</BODY></HTML>");
              }
          接受servlet
          public class NileItem extends HttpServlet {

            public void doGet(HttpServletRequest req, HttpServletResponse res)
                                         throws ServletException, IOException {

              // We do not set the content type

              PrintWriter out = res.getWriter();

              Book book = (Book) req.getAttribute("item");

              out.println("<BR>");
              if (book != null) {
                out.println("<I>" + book.getTitle() + "</I>");
                out.println(" by " + book.getAuthor());
              }
              else {
                out.println("<I>No book record found</I>");
              }
              out.println("<BR>");
            }
          }
          通過實例可以很好理解include,就是把下一個資源的內容直接放入本頁面的任何地方,被包含的資源不能改變status code or HTTP headers ,所以NileItem 做任何改變這些的操作都會被忽略(NileItem 所以沒有做這些操作)。不像forward,路徑元素和請求參數仍然和調用端一樣。如果被包含資源要求獲得自己的路徑元素和請求參數, it may retrieve them using the following server-assigned request attributes:
          javax.servlet.include.request_uri
          javax.servlet.include.context_path
          javax.servlet.include.servlet_path
          javax.servlet.include.path_info

          The request and response parameters must be the same objects as were passed to the calling servlet's service method, and the include( ) must be called from within the same handler thread.

          The included resource must use an output mechanism that matches the caller's. If the caller uses a PrintWriter, then the included resource must use a PrintWriter. If the caller uses an OutputStream, then the included resource must use an OutputStream. If the mechanisms don't match, the included servlet throws an IllegalStateException. If you can, make sure to use the PrintWriter for all text output, and this won't be a problem.

          posted @ 2009-11-05 17:11 yuxh 閱讀(1711) | 評論 (0)編輯 收藏

          servlet沒有main方法,他受一個叫做容器的java應用(比如Tomcat)控制。當web server應用(比如Apache)收到容器訪問servlet請求,server不是先把請求直接給servlet,而是先給部署這個servlet的容器,容器“看到”找的是servlet,創建兩個對象:HttpServletResponse和HttpServletRequest。容器根據URL找到servlet類,為這個請求創建或從池里分配線程,把request和response對象傳給該線程,servlet的生命周期就開始了。
                 容器啟動的時候這步就發生了,一啟動容器就開始找部署的web項目。第一尋找其servlet類,第二就開始加載這個類。A servlet moves from does not exist to initialized (which really means ready to service client requests), beginning with a constructor. But the constructor makes only an object, not a servlet. To be a servlet, the object needs to be granted servletness. 所以在構造函數和init()之間的狀態是一種“半死半活狀態”,這時候不能接觸很多作為servlet能接觸的資源(like getting web app configuration info, or looking up a reference to another part of the application),所以init()之前都不要做什么操作,不要在構造函數中放東西。在創建servlet實例之后在為任何客戶端請求服務之前,容器就調用init方法,你可以在處理請求之前在里面初始化你的servlet(重寫此方法可以建立數據庫連接,用其他對象注冊自己等)。接著調用service方法(重寫此方法的可能性不大),接著doGet/doPost(至少重寫其中一個方法)。service結束后,線程就消亡或者放回池里。但第二個請求來,容器再次創建或從池里分配線程,繼續service->doGet/doPost 序列。因此,在一個時間段內,有多少客戶請求就有多少運行的線程,當然也受容器的策略、配置或資源限制(比如指定容器的最大并發線程數,當請求的客戶數超過的時候,客戶就必須等待)。容器運行多個線程來處理多個對一個servlet的請求(不管是不是同一個客戶發出請求,一個請求一個線程),對于分布式web應用,一個JVM只對應一個servlet實例
                 成為一個servlet可以給你什么:一個ServletConfig對象,一個ServletContext。
                  ServletConfig對象:每個servlet有一個;Use it to pass deploy-time information to the servlet (a database or enterprise bean lookup name, for example)that you don’t want to hard-code into the servlet (servlet init parameters);訪問ServletContext;Parameters are configured in the Deployment Descriptor(如<init-param>方式配置).我們可以通過servletconfig獲得初始化參數(getServletConfig().getInitParameter(“foo”);),然后通過setAttribute dispatch給某個jsp頁面,但是針對一個servlet的初始化參數很有局限性,所以引入針對應用的<context-param>,這時候通過servletContext獲得初始化參數(getServletContext().getInitParameter(“foo”)),這個初始化參數可以被應用的任意servlet,jsp訪問。注意初始化參數是一個deploy-time constants,不能在runtime改變他們,只能通過重新部署

                 ServletContext :每個web app有一個(嚴格說來如果是分布式的話,應該是每個JVM一個);Use it to access web app parameters (also confi gured in the Deployment Descriptor) ;類似application bulletin-board功能,讓其他應用也能訪問;用來獲得服務器信息,比如容器名字和版本,以及支持的API版本。ServletContextListener 可以監聽ServletContext 的初始化(從ServletContext中獲得初始化參數,用參數連接數據庫,把連接作為attribute存起來以便所有應用都可以訪問)和銷毀階段(關閉連接)。為了使context attributes 線程安全,可以對context進行同步鎖的方式:

          synchronized(getServletContext()) {
               getServletContext().setAttribute(“foo”, “22”);
               getServletContext().setAttribute(“bar”, “42”);
               out.println(getServletContext().getAttribute(“foo”));
               out.println(getServletContext().getAttribute(“bar”));
           }
          

                 ServletResponse接口有兩種流可供輸出:ServletOutputStream for bytes, or a PrintWriter for character data.
          例:1   PrintWriter writer = response.getWriter();
                     writer.println(“some text and HTML”);
                2   ServletOutputStream out = response.getOutputStream();
                     out.write(aByteArray);
                  如果不想自己處理,可通過redirect給一個全新的URL(注意如果response已提交后不能再redirect),或者dispatch the request 給你應用中的某個組件(如jsp)。redirect會先返回給客戶,客戶再重新發請求,而dispatch 的話,服務器會直接轉給某個組件。這樣兩種方式的區別就很明顯了。

          servlet->容器調用service()->doGet()/doPost() 產生動態頁面并把頁面放入response對象,記住此時容器仍然有引用指向該response對象!線程完成,容器把response對象轉換為HTTP對象,送回客戶端,然后刪除request和response對象。
          posted @ 2009-05-15 10:22 yuxh 閱讀(261) | 評論 (0)編輯 收藏
          jsp中的所有東西都可分為兩大類:elementstemplate data。elements 是jsp的動態部分,他們按照jsp規范作為自定義操作,標簽,以及之間的允許內容,jsp容器會解釋elements ,當產生response的時候elements 定義的操作會運行(dynamic)。template data是靜態文本部分,他們被任意放在jsp頁面上直接發送給客戶端(not change)。
                elements 又可分為三類:scripting elements, directives, and actions

          scripting elements :有三種類型:
                1 Scriptlets即常見到的<%.... %>,注意scriptlet 的內容不會發送給客戶端,只有scriptlet 的結果會發送,這表明scriptlet 被容器解釋,scriptlet 的代碼默認情況下不會被訪問者分享。 代碼編譯成servlet后在jspService() 內。這里面寫的java代碼最后結果以out.print(…);輸出
               2 Expressions:<%=...=>: 發送字符串給客戶端,表達式可以是任意有toString方法的對象,或者是primitive。代碼編譯成servlet后在jspService() 內。不要在后面加分號,否則會以out.print(...;); 形式輸出,引起編譯錯誤
             3 Declarations:<!=...=>: declaration 就像scriptlet一樣用來向jsp嵌入代碼,這些聲明編譯成servlet后都是在jspService() 方法外面的,所以聲明里可用來定義新方法(可以被scriptlet調用)或全局類變量,但聲明里代碼不是線程安全的,必須自己控制。


          directives  并不用來向客戶端輸出,用來定義頁面屬性,包括定制標簽庫和包含其他頁面,語法 :
               <%@ directive {attribute="value"}* %>
          三種不同的jsp directives :page, taglib, and include。
            <%@ page %>給jsp容器提供頁面的相關信息
          <%@ include %> is used to include text and/or code at translation time of a JSP,語法<%@ include file="relativeURL" %>,這個文件必須是web 應用的一部分,由于在轉換階段被包括,相當于在編譯之前把文件源代碼直接放進來,所以不影響runtime階段的性能。<jsp:include /> 在runtime階段觸發,雖然效能沒前面translation time include好,但保證被包括文件的實時性。
          <%@ taglib %> 自定義標簽庫,語法<%@ taglib uri="uri" prefix="prefixOfTag" %>

          Actions  可以方便的把動態代碼鏈入簡單的jsp頁面,功能類似scripting elements ,但是把和jsp打交道的java代碼抽象出來了,有兩種:standard and custom 語法 <prefix:element {attribute="value"}* />

          jsp中有implicit objects,就像在Servlets中的對象,在scripting elements中可以使用:config,request,response,session(默認jsp持有一個session,在scripting elements 用這個對象,就像調用HttpServletRequest getSession() 一樣),application(javax.servlet.ServletContext 對象實例,就像調用ServletConfig getServletContext() 一樣)
          還有一些沒有直接和Servlet 對應的,但是隱含的對象:pageContext(含有一個jsp的許多對象資源信息,在定制標簽的時候用得較多), page, out, and exception
          posted @ 2009-05-15 10:21 yuxh 閱讀(180) | 評論 (0)編輯 收藏
          僅列出標題
          共4頁: 上一頁 1 2 3 4 下一頁 

          導航

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          統計

          • 隨筆 - 48
          • 文章 - 0
          • 評論 - 2
          • 引用 - 0

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          收藏夾

          博客

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 武冈市| 钟山县| 宝兴县| 凤翔县| 麻城市| 福州市| 库伦旗| 蓝山县| 镇雄县| 嘉峪关市| 正蓝旗| 吉林市| 汉寿县| 醴陵市| 望谟县| 屯昌县| 洪雅县| 正阳县| 资源县| 桃源县| 德化县| 大同市| 山阳县| 宜都市| 江城| 金塔县| 罗源县| 湾仔区| 乃东县| 赤壁市| 平安县| 阿城市| 兴仁县| 阿尔山市| 长沙县| 扶绥县| 任丘市| 安阳市| 资兴市| 沾化县| 德令哈市|