<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    bt下載與小說520

    bt下載與小說520

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      16 隨筆 :: 0 文章 :: 6 評論 :: 0 Trackbacks

    #

    第1章基礎知識

    1.1. 單鑰密碼體制

    單鑰密碼體制是一種傳統的加密算法,是指信息的發送方和接收方共同使用同一把密鑰進行加解密。

    通常,使用的加密算法比較簡便高效,密鑰簡短,加解密速度快,破譯極其困難。但是加密的安全性依靠密鑰保管的安全性,在公開的計算機網絡上安全地傳送和保管密鑰是一個嚴峻的問題,并且如果在多用戶的情況下密鑰的保管安全性也是一個問題。

    單鑰密碼體制的代表是美國的DES

    1.2. 消息摘要

    一個消息摘要就是一個數據塊的數字指紋。即對一個任意長度的一個數據塊進行計算,產生一個唯一指?。▽τ赟HA1是產生一個20字節的二進制數組)。

    消息摘要有兩個基本屬性:

    • 兩個不同的報文難以生成相同的摘要
    • 難以對指定的摘要生成一個報文,而由該報文反推算出該指定的摘要

    代表:美國國家標準技術研究所的SHA1和麻省理工學院Ronald Rivest提出的MD5

    1.3. Diffie-Hellman密鑰一致協議

    密鑰一致協議是由公開密鑰密碼體制的奠基人Diffie和Hellman所提出的一種思想。

    先決條件,允許兩名用戶在公開媒體上交換信息以生成"一致"的,可以共享的密鑰

    代表:指數密鑰一致協議(Exponential Key Agreement Protocol)

    1.4. 非對稱算法與公鑰體系

    1976年,Dittie和Hellman為解決密鑰管理問題,在他們的奠基性的工作"密碼學的新方向"一文中,提出一種密鑰交換協議,允許在不安全的媒體上通過通訊雙方交換信息,安全地傳送秘密密鑰。在此新思想的基礎上,很快出現了非對稱密鑰密碼體制,即公鑰密碼體制。在公鑰體制中,加密密鑰不同于解密密鑰,加密密鑰公之于眾,誰都可以使用;解密密鑰只有解密人自己知道。它們分別稱為公開密鑰(Public key)和秘密密鑰(Private key)。

    迄今為止的所有公鑰密碼體系中,RSA系統是最著名、最多使用的一種。RSA公開密鑰密碼系統是由R.Rivest、A.Shamir和L.Adleman俊教授于1977年提出的。RSA的取名就是來自于這三位發明者的姓的第一個字母

    1.5. 數字簽名

    所謂數字簽名就是信息發送者用其私鑰對從所傳報文中提取出的特征數據(或稱數字指紋)進行RSA算法操作,以保證發信人無法抵賴曾發過該信息(即不可抵賴性),同時也確保信息報文在經簽名后末被篡改(即完整性)。當信息接收者收到報文后,就可以用發送者的公鑰對數字簽名進行驗證?!?/p>

    在數字簽名中有重要作用的數字指紋是通過一類特殊的散列函數(HASH函數)生成的,對這些HASH函數的特殊要求是:

    1. 接受的輸入報文數據沒有長度限制;
    2. 對任何輸入報文數據生成固定長度的摘要(數字指紋)輸出
    3. 從報文能方便地算出摘要;
    4. 難以對指定的摘要生成一個報文,而由該報文反推算出該指定的摘要;
    5. 兩個不同的報文難以生成相同的摘要

    代表:DSA





    回頁首


    第2章在JAVA中的實現

    2.1. 相關

    Diffie-Hellman密鑰一致協議和DES程序需要JCE工具庫的支持,可以到 http://java.sun.com/security/index.html  或是www.bt285.cn 下載JCE,并進行安裝。簡易安裝把 jce1.2.1\lib 下的所有內容復制到 %java_home%\lib\ext下,如果沒有ext目錄自行建立,再把jce1_2_1.jar和sunjce_provider.jar添加到CLASSPATH內,更詳細說明請看相應用戶手冊

    2.2. 消息摘要MD5和SHA的使用

    使用方法:

    首先用生成一個MessageDigest類,確定計算方法

    java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");

    添加要進行計算摘要的信息

    alga.update(myinfo.getBytes());

    計算出摘要

    byte[] digesta=alga.digest();

    發送給其他人你的信息和摘要

    其他人用相同的方法初始化,添加信息,最后進行比較摘要是否相同

    algb.isEqual(digesta,algb.digest())

    相關AIP

    java.security.MessageDigest 類

    static getInstance(String algorithm)

    返回一個MessageDigest對象,它實現指定的算法

    參數:算法名,如 SHA-1 或MD5

    void update (byte input)

    void update (byte[] input)

    void update(byte[] input, int offset, int len)

    添加要進行計算摘要的信息

    byte[] digest()

    完成計算,返回計算得到的摘要(對于MD5是16位,SHA是20位)

    void reset()

    復位

    static boolean isEqual(byte[] digesta, byte[] digestb)

    比效兩個摘要是否相同

    代碼:

    import java.security.*;
                public class myDigest {
                public static void main(String[] args)  {
                myDigest my=new myDigest();
                my.testDigest();
                }
                public void testDigest()
                {
                try {
                String myinfo="我的測試信息";
                //java.security.MessageDigest alg=java.security.MessageDigest.getInstance("MD5");
                java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");
                alga.update(myinfo.getBytes());
                byte[] digesta=alga.digest();
                System.out.println("本信息摘要是:"+byte2hex(digesta));
                //通過某中方式傳給其他人你的信息(myinfo)和摘要(digesta) 對方可以判斷是否更改或傳輸正常
                java.security.MessageDigest algb=java.security.MessageDigest.getInstance("SHA-1");
                algb.update(myinfo.getBytes());
                if (algb.isEqual(digesta,algb.digest())) {
                System.out.println("信息檢查正常");
                }
                else
                {
                System.out.println("摘要不相同");
                }
                }
                catch (java.security.NoSuchAlgorithmException ex) {
                System.out.println("非法摘要算法");
                }
                }
                public String byte2hex(byte[] b) //二行制轉字符串
                {
                String hs="";
                String stmp="";
                for (int n=0;n<b.length;n++)
                {
                stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
                if (stmp.length()==1) hs=hs+"0"+stmp;
                else hs=hs+stmp;
                if (n<b.length-1)  hs=hs+":";
                }
                return hs.toUpperCase();
                }
                }

    2.3. 數字簽名DSA

    1. 對于一個用戶來講首先要生成他的密鑰對,并且分別保存

      生成一個KeyPairGenerator實例

         java.security.KeyPairGenerator  keygen=java.security.KeyPairGenerator.getInstance("DSA");
                          如果設定隨機產生器就用如相代碼初始化
                      SecureRandom secrand=new SecureRandom();
                      secrand.setSeed("tttt".getBytes()); //初始化隨機產生器
                      keygen.initialize(512,secrand);     //初始化密鑰生成器
                      否則
                      keygen.initialize(512);
                      生成密鑰公鑰pubkey和私鑰prikey
                      KeyPair keys=keygen.generateKeyPair(); //生成密鑰組
                      PublicKey pubkey=keys.getPublic();
                      PrivateKey prikey=keys.getPrivate();
                      分別保存在myprikey.dat和mypubkey.dat中,以便下次不在生成
                      (生成密鑰對的時間比較長
                      java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));
                           out.writeObject(prikey);
                      out.close();
                      out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));
                           out.writeObject(pubkey);
                      out.close();
                      


    2. 用他私人密鑰(prikey)對他所確認的信息(info)進行數字簽名產生一個簽名數組

      從文件中讀入私人密鑰(prikey)

         java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));
                          PrivateKey myprikey=(PrivateKey)in.readObject();
                      in.close();
                      初始一個Signature對象,并用私鑰對信息簽名
                      java.security.Signature signet=java.security.Signature.getInstance("DSA");
                      signet.initSign(myprikey);
                      signet.update(myinfo.getBytes());
                      byte[] signed=signet.sign();
                      把信息和簽名保存在一個文件中(myinfo.dat)
                      java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));
                            out.writeObject(myinfo);
                      out.writeObject(signed);
                      out.close();
                      把他的公鑰的信息及簽名發給其它用戶
                      


    3. 其他用戶用他的公共密鑰(pubkey)和簽名(signed)和信息(info)進行驗證是否由他簽名的信息

      讀入公鑰
      java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("mypubkey.dat"));
      PublicKey pubkey=(PublicKey)in.readObject();
      in.close();

      讀入簽名和信息
      in=new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat"));
      String info=(String)in.readObject();
      byte[] signed=(byte[])in.readObject();
      in.close();

      初始一個Signature對象,并用公鑰和簽名進行驗證
      java.security.Signature signetcheck=java.security.Signature.getInstance("DSA");
      signetcheck.initVerify(pubkey);
      signetcheck.update(info.getBytes());
      if (signetcheck.verify(signed)) { System.out.println("簽名正常");}

      對于密鑰的保存本文是用對象流的方式保存和傳送的,也可可以用編碼的方式保存.注意要
      import java.security.spec.*
      import java.security.*

      具休說明如下

      • public key是用X.509編碼的,例碼如下:
          byte[] bobEncodedPubKey=mypublic.getEncoded(); //生成編碼
                            //傳送二進制編碼
                            //以下代碼轉換編碼為相應key對象
                            X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
                            KeyFactory keyFactory = KeyFactory.getInstance("DSA");
                            PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);
                            


      • 對于Private key是用PKCS#8編碼,例碼如下:
         byte[] bPKCS=myprikey.getEncoded();
                            //傳送二進制編碼
                            //以下代碼轉換編碼為相應key對象
                            PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(bPKCS);
                            KeyFactory keyf=KeyFactory.getInstance("DSA");
                            PrivateKey otherprikey=keyf.generatePrivate(priPKCS8);
                            


    4. 常用API

      java.security.KeyPairGenerator 密鑰生成器類
      public static KeyPairGenerator getInstance(String algorithm) throws NoSuchAlgorithmException
      以指定的算法返回一個KeyPairGenerator 對象
      參數: algorithm 算法名.如:"DSA","RSA"

      public void initialize(int keysize)

      以指定的長度初始化KeyPairGenerator對象,如果沒有初始化系統以1024長度默認設置

      參數:keysize 算法位長.其范圍必須在 512 到 1024 之間,且必須為 64 的倍數

      public void initialize(int keysize, SecureRandom random)
      以指定的長度初始化和隨機發生器初始化KeyPairGenerator對象
      參數:keysize 算法位長.其范圍必須在 512 到 1024 之間,且必須為 64 的倍數
      random 一個隨機位的來源(對于initialize(int keysize)使用了默認隨機器

      public abstract KeyPair generateKeyPair()
      產生新密鑰對

      java.security.KeyPair 密鑰對類
      public PrivateKey getPrivate()
      返回私鑰

      public PublicKey getPublic()
      返回公鑰

      java.security.Signature 簽名類
      public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException
      返回一個指定算法的Signature對象
      參數 algorithm 如:"DSA"

      public final void initSign(PrivateKey privateKey)
      throws InvalidKeyException
      用指定的私鑰初始化
      參數:privateKey 所進行簽名時用的私鑰

      public final void update(byte data)
      throws SignatureException
      public final void update(byte[] data)
      throws SignatureException
      public final void update(byte[] data, int off, int len)
      throws SignatureException
      添加要簽名的信息

      public final byte[] sign()
      throws SignatureException
      返回簽名的數組,前提是initSign和update

      public final void initVerify(PublicKey publicKey)
      throws InvalidKeyException
      用指定的公鑰初始化
      參數:publicKey 驗證時用的公鑰

      public final boolean verify(byte[] signature)
      throws SignatureException
      驗證簽名是否有效,前提是已經initVerify初始化
      參數: signature 簽名數組

       */
                      import java.security.*;
                      import java.security.spec.*;
                      public class testdsa {
                      public static void main(String[] args) throws java.security.NoSuchAlgorithmException,java.lang.Exception {
                              testdsa my=new testdsa();
                      my.run();
                      }
                      public void run()
                      {
                      //數字簽名生成密鑰
                      //第一步生成密鑰對,如果已經生成過,本過程就可以跳過,對用戶來講myprikey.dat要保存在本地
                      //而mypubkey.dat給發布給其它用戶
                      if ((new java.io.File("myprikey.dat")).exists()==false) {
                      if (generatekey()==false) {
                      System.out.println("生成密鑰對敗");
                      return;
                      };
                      }
                      //第二步,此用戶
                      //從文件中讀入私鑰,對一個字符串進行簽名后保存在一個文件(myinfo.dat)中
                      //并且再把myinfo.dat發送出去
                      //為了方便數字簽名也放進了myifno.dat文件中,當然也可分別發送
                      try {
                      java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));
                        PrivateKey myprikey=(PrivateKey)in.readObject();
                      in.close();
                      // java.security.spec.X509EncodedKeySpec pubX509=new java.security.spec.X509EncodedKeySpec(bX509);
                       //java.security.spec.X509EncodedKeySpec pubkeyEncode=java.security.spec.X509EncodedKeySpec
                        String myinfo="這是我的信息";    //要簽名的信息
                      //用私鑰對信息生成數字簽名
                      java.security.Signature signet=java.security.Signature.getInstance("DSA");
                      signet.initSign(myprikey);
                      signet.update(myinfo.getBytes());
                      byte[] signed=signet.sign();  //對信息的數字簽名
                      System.out.println("signed(簽名內容)="+byte2hex(signed));
                      //把信息和數字簽名保存在一個文件中
                      java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));
                        out.writeObject(myinfo);
                      out.writeObject(signed);
                      out.close();
                      System.out.println("簽名并生成文件成功");
                      }
                      catch (java.lang.Exception e) {
                      e.printStackTrace();
                      System.out.println("簽名并生成文件失敗");
                      };
                      //第三步
                      //其他人通過公共方式得到此戶的公鑰和文件
                      //其他人用此戶的公鑰,對文件進行檢查,如果成功說明是此用戶發布的信息.
                      //
                      try {
                      java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("mypubkey.dat"));
                         PublicKey pubkey=(PublicKey)in.readObject();
                      in.close();
                      System.out.println(pubkey.getFormat());
                      in=new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat"));
                      String info=(String)in.readObject();
                      byte[] signed=(byte[])in.readObject();
                      in.close();
                      java.security.Signature signetcheck=java.security.Signature.getInstance("DSA");
                      signetcheck.initVerify(pubkey);
                      signetcheck.update(info.getBytes());
                      if (signetcheck.verify(signed)) {
                      System.out.println("info="+info);
                      System.out.println("簽名正常");
                      }
                      else  System.out.println("非簽名正常");
                      }
                      catch (java.lang.Exception e) {e.printStackTrace();};
                      }
                      //生成一對文件myprikey.dat和mypubkey.dat---私鑰和公鑰,
                      //公鑰要用戶發送(文件,網絡等方法)給其它用戶,私鑰保存在本地
                      public boolean generatekey()
                      {
                      try {
                      java.security.KeyPairGenerator  keygen=java.security.KeyPairGenerator.getInstance("DSA");
                       // SecureRandom secrand=new SecureRandom();
                      // secrand.setSeed("tttt".getBytes()); //初始化隨機產生器
                      // keygen.initialize(576,secrand);     //初始化密鑰生成器
                      keygen.initialize(512);
                      KeyPair keys=keygen.genKeyPair();
                      //  KeyPair keys=keygen.generateKeyPair(); //生成密鑰組
                      PublicKey pubkey=keys.getPublic();
                      PrivateKey prikey=keys.getPrivate();
                      java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));
                        out.writeObject(prikey);
                      out.close();
                      System.out.println("寫入對象 prikeys ok");
                      out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));
                      out.writeObject(pubkey);
                      out.close();
                      System.out.println("寫入對象 pubkeys ok");
                      System.out.println("生成密鑰對成功");
                      return true;
                      }
                      catch (java.lang.Exception e) {
                      e.printStackTrace();
                      System.out.println("生成密鑰對失敗");
                      return false;
                      };
                      }
                      public String byte2hex(byte[] b)
                      {
                      String hs="";
                      String stmp="";
                      for (int n=0;n<b.length;n++)
                      {
                      stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
                      if (stmp.length()==1) hs=hs+"0"+stmp;
                      else hs=hs+stmp;
                      if (n<b.length-1)  hs=hs+":";
                      }
                      return hs.toUpperCase();
                      }
                      }


    2.4. DESede/DES對稱算法

    首先生成密鑰,并保存(這里并沒的保存的代碼,可參考DSA中的方法)

    KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);

    SecretKey deskey = keygen.generateKey();

    用密鑰加密明文(myinfo),生成密文(cipherByte)

    Cipher c1 = Cipher.getInstance(Algorithm);

    c1.init(Cipher.ENCRYPT_MODE,deskey);

    byte[] cipherByte=c1.doFinal(myinfo.getBytes());

    傳送密文和密鑰,本文沒有相應代碼可參考DSA

    .............

    用密鑰解密密文

    c1 = Cipher.getInstance(Algorithm);

    c1.init(Cipher.DECRYPT_MODE,deskey);

    byte[] clearByte=c1.doFinal(cipherByte);

    相對來說對稱密鑰的使用是很簡單的,對于JCE來講支技DES,DESede,Blowfish三種加密術

    對于密鑰的保存各傳送可使用對象流或者用二進制編碼,相關參考代碼如下

       SecretKey deskey = keygen.generateKey();
                byte[] desEncode=deskey.getEncoded();
                javax.crypto.spec.SecretKeySpec destmp=new javax.crypto.spec.SecretKeySpec(desEncode,Algorithm);
                   SecretKey mydeskey=destmp;

    相關API

    KeyGenerator 在DSA中已經說明,在添加JCE后在instance進可以如下參數

    DES,DESede,Blowfish,HmacMD5,HmacSHA1

    javax.crypto.Cipher 加/解密器

    public static final Cipher getInstance(java.lang.String transformation)
                throws java.security.NoSuchAlgorithmException,
                NoSuchPaddingException

    返回一個指定方法的Cipher對象

    參數:transformation 方法名(可用 DES,DESede,Blowfish)

    public final void init(int opmode, java.security.Key key)
    throws java.security.InvalidKeyException

    用指定的密鑰和模式初始化Cipher對象

    參數:opmode 方式(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)

    key 密鑰

    public final byte[] doFinal(byte[] input)
                throws java.lang.IllegalStateException,
                IllegalBlockSizeException,
                BadPaddingException
                

    對input內的串,進行編碼處理,返回處理后二進制串,是返回解密文還是加解文由init時的opmode決定

    注意:本方法的執行前如果有update,是對updat和本次input全部處理,否則是本inout的內容

    /*
                安全程序 DESede/DES測試
                */
                import java.security.*;
                import javax.crypto.*;
                public class testdes {
                public static void main(String[] args){
                testdes my=new testdes();
                my.run();
                }
                public  void run() {
                //添加新安全算法,如果用JCE就要把它添加進去
                Security.addProvider(new com.sun.crypto.provider.SunJCE());
                String Algorithm="DES"; //定義 加密算法,可用 DES,DESede,Blowfish
                String myinfo="要加密的信息";
                try {
                //生成密鑰
                KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);
                SecretKey deskey = keygen.generateKey();
                //加密
                System.out.println("加密前的二進串:"+byte2hex(myinfo.getBytes()));
                System.out.println("加密前的信息:"+myinfo);
                Cipher c1 = Cipher.getInstance(Algorithm);
                c1.init(Cipher.ENCRYPT_MODE,deskey);
                byte[] cipherByte=c1.doFinal(myinfo.getBytes());
                System.out.println("加密后的二進串:"+byte2hex(cipherByte));
                //解密
                c1 = Cipher.getInstance(Algorithm);
                c1.init(Cipher.DECRYPT_MODE,deskey);
                byte[] clearByte=c1.doFinal(cipherByte);
                System.out.println("解密后的二進串:"+byte2hex(clearByte));
                System.out.println("解密后的信息:"+(new String(clearByte)));
                }
                catch (java.security.NoSuchAlgorithmException e1) {e1.printStackTrace();}
                catch (javax.crypto.NoSuchPaddingException e2) {e2.printStackTrace();}
                catch (java.lang.Exception e3) {e3.printStackTrace();}
                }
                public String byte2hex(byte[] b) //二行制轉字符串
                {
                String hs="";
                String stmp="";
                for (int n=0;n<b.length;n++)
                {
                stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
                if (stmp.length()==1) hs=hs+"0"+stmp;
                else hs=hs+stmp;
                if (n<b.length-1)  hs=hs+":";
                }
                return hs.toUpperCase();
                }
                }


    2.5. Diffie-Hellman密鑰一致協議

    公開密鑰密碼體制的奠基人Diffie和Hellman所提出的 "指數密鑰一致協議"(Exponential Key Agreement Protocol),該協議不要求別的安全性先決條件,允許兩名用戶在公開媒體上交換信息以生成"一致"的,可以共享的密鑰。在JCE的中實現用戶alice生成DH類型的密鑰對,如果長度用1024生成的時間請,推薦第一次生成后保存DHParameterSpec,以便下次使用直接初始化.使其速度加快

    System.out.println("ALICE: 產生 DH 對 ...");
                KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
                aliceKpairGen.initialize(512);
                KeyPair aliceKpair = aliceKpairGen.generateKeyPair();

    alice生成公鑰發送組bob

    byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();

    bob從alice發送來的公鑰中讀出DH密鑰對的初始參數生成bob的DH密鑰對

    注意這一步一定要做,要保證每個用戶用相同的初始參數生成的

       DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();
                KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
                bobKpairGen.initialize(dhParamSpec);
                KeyPair bobKpair = bobKpairGen.generateKeyPair();

    bob根據alice的公鑰生成本地的DES密鑰

       KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
                bobKeyAgree.init(bobKpair.getPrivate());
                bobKeyAgree.doPhase(alicePubKey, true);
                SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");

    bob已經生成了他的DES密鑰,他現把他的公鑰發給alice,

          byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();

    alice根據bob的公鑰生成本地的DES密鑰

           ,,,,,,解碼
                KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
                aliceKeyAgree.init(aliceKpair.getPrivate());
                aliceKeyAgree.doPhase(bobPubKey, true);
                SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");

    bob和alice能過這個過程就生成了相同的DES密鑰,在這種基礎就可進行安全能信

    常用API

    java.security.KeyPairGenerator 密鑰生成器類
    public static KeyPairGenerator getInstance(String algorithm)
    throws NoSuchAlgorithmException
    以指定的算法返回一個KeyPairGenerator 對象
    參數: algorithm 算法名.如:原來是DSA,現在添加了 DiffieHellman(DH)

    public void initialize(int keysize)
    以指定的長度初始化KeyPairGenerator對象,如果沒有初始化系統以1024長度默認設置
    參數:keysize 算法位長.其范圍必須在 512 到 1024 之間,且必須為 64 的倍數
    注意:如果用1024生長的時間很長,最好生成一次后就保存,下次就不用生成了

    public void initialize(AlgorithmParameterSpec params)
    throws InvalidAlgorithmParameterException
    以指定參數初始化

    javax.crypto.interfaces.DHPublicKey
    public DHParameterSpec getParams()
    返回
    java.security.KeyFactory

    public static KeyFactory getInstance(String algorithm)
    throws NoSuchAlgorithmException
    以指定的算法返回一個KeyFactory
    參數: algorithm 算法名:DSH,DH

    public final PublicKey generatePublic(KeySpec keySpec)
    throws InvalidKeySpecException
    根據指定的key說明,返回一個PublicKey對象

    java.security.spec.X509EncodedKeySpec
    public X509EncodedKeySpec(byte[] encodedKey)
    根據指定的二進制編碼的字串生成一個key的說明
    參數:encodedKey 二進制編碼的字串(一般能過PublicKey.getEncoded()生成)
    javax.crypto.KeyAgreement 密碼一至類

    public static final KeyAgreement getInstance(java.lang.String algorithm)
    throws java.security.NoSuchAlgorithmException
    返回一個指定算法的KeyAgreement對象
    參數:algorithm 算法名,現在只能是DiffieHellman(DH)

    public final void init(java.security.Key key)
    throws java.security.InvalidKeyException
    用指定的私鑰初始化
    參數:key 一個私鑰

    public final java.security.Key doPhase(java.security.Key key,
    boolean lastPhase)
    throws java.security.InvalidKeyException,
    java.lang.IllegalStateException
    用指定的公鑰進行定位,lastPhase確定這是否是最后一個公鑰,對于兩個用戶的
    情況下就可以多次定次,最后確定
    參數:key 公鑰
    lastPhase 是否最后公鑰

    public final SecretKey generateSecret(java.lang.String algorithm)
    throws java.lang.IllegalStateException,
    java.security.NoSuchAlgorithmException,
    java.security.InvalidKeyException
    根據指定的算法生成密鑰
    參數:algorithm 加密算法(可用 DES,DESede,Blowfish)

    */
                import java.io.*;
                import java.math.BigInteger;
                import java.security.*;
                import java.security.spec.*;
                import java.security.interfaces.*;
                import javax.crypto.*;
                import javax.crypto.spec.*;
                import javax.crypto.interfaces.*;
                import com.sun.crypto.provider.SunJCE;
                public class testDHKey {
                public static void main(String argv[]) {
                try {
                testDHKey my= new testDHKey();
                my.run();
                } catch (Exception e) {
                System.err.println(e);
                }
                }
                private void run() throws Exception {
                Security.addProvider(new com.sun.crypto.provider.SunJCE());
                System.out.println("ALICE: 產生 DH 對 ...");
                KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
                aliceKpairGen.initialize(512);
                KeyPair aliceKpair = aliceKpairGen.generateKeyPair(); //生成時間長
                // 張三(Alice)生成公共密鑰 alicePubKeyEnc 并發送給李四(Bob) ,
                //比如用文件方式,socket.....
                byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();
                //bob接收到alice的編碼后的公鑰,將其解碼
                KeyFactory bobKeyFac = KeyFactory.getInstance("DH");
                X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec  (alicePubKeyEnc);
                PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);
                System.out.println("alice公鑰bob解碼成功");
                // bob必須用相同的參數初始化的他的DH KEY對,所以要從Alice發給他的公開密鑰,
                //中讀出參數,再用這個參數初始化他的 DH key對
                //從alicePubKye中取alice初始化時用的參數
                DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();
                KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
                bobKpairGen.initialize(dhParamSpec);
                KeyPair bobKpair = bobKpairGen.generateKeyPair();
                System.out.println("BOB: 生成 DH key 對成功");
                KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
                bobKeyAgree.init(bobKpair.getPrivate());
                System.out.println("BOB: 初始化本地key成功");
                //李四(bob) 生成本地的密鑰 bobDesKey
                bobKeyAgree.doPhase(alicePubKey, true);
                SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");
                System.out.println("BOB: 用alice的公鑰定位本地key,生成本地DES密鑰成功");
                // Bob生成公共密鑰 bobPubKeyEnc 并發送給Alice,
                //比如用文件方式,socket.....,使其生成本地密鑰
                byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();
                System.out.println("BOB向ALICE發送公鑰");
                // alice接收到 bobPubKeyEnc后生成bobPubKey
                // 再進行定位,使aliceKeyAgree定位在bobPubKey
                KeyFactory aliceKeyFac = KeyFactory.getInstance("DH");
                x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
                PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
                System.out.println("ALICE接收BOB公鑰并解碼成功");
                ;
                KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
                aliceKeyAgree.init(aliceKpair.getPrivate());
                System.out.println("ALICE: 初始化本地key成功");
                aliceKeyAgree.doPhase(bobPubKey, true);
                // 張三(alice) 生成本地的密鑰 aliceDesKey
                SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");
                System.out.println("ALICE: 用bob的公鑰定位本地key,并生成本地DES密鑰");
                if (aliceDesKey.equals(bobDesKey)) System.out.println("張三和李四的密鑰相同");
                //現在張三和李四的本地的deskey是相同的所以,完全可以進行發送加密,接收后解密,達到
                //安全通道的的目的
                /*
                * bob用bobDesKey密鑰加密信息
                */
                Cipher bobCipher = Cipher.getInstance("DES");
                bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);
                String bobinfo= "這是李四的機密信息";
                System.out.println("李四加密前原文:"+bobinfo);
                byte[] cleartext =bobinfo.getBytes();
                byte[] ciphertext = bobCipher.doFinal(cleartext);
                /*
                * alice用aliceDesKey密鑰解密
                */
                Cipher aliceCipher = Cipher.getInstance("DES");
                aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey);
                byte[] recovered = aliceCipher.doFinal(ciphertext);
                System.out.println("alice解密bob的信息:"+(new String(recovered)));
                if (!java.util.Arrays.equals(cleartext, recovered))
                throw new Exception("解密后與原文信息不同");
                System.out.println("解密后相同");
                }
                }
    posted @ 2008-10-13 19:31 bt下載| 編輯 收藏

    關于應用服務器和web服務器的整合,有很多的資料了,可是都講的半生不熟的。根據這幾天整合tomcat 和 iis 的經驗,再次聊聊這個話題。

    首先我們應該對應用服務器和web服務器有一個清晰的概念。所謂的應用服務器,就是提供應用的服務器,這里的應用有很多,比如java應用,ruby 應用,或者 c#應用。

    那么什么是web服務器呢?就是提供了web功能的服務器,主要就是http服務,包括圖片的下載,等等一系列和web相關的。

    好吧,你會問為什么我們不能直接使用應用服務器呢?應用服務器也提供了http服務,比如tomcat。

    那么我們從實際出發。當你瀏覽一個網頁的時候,什么情況下你會覺得速度很慢?我們僅僅考慮頁面本身。那當然是圖片越多顯示得越慢。

    好吧,我們至少認識到一點,一些靜態資源,例如圖片,會嚴重影響頁面打開的速度。當然,這僅僅是一個方面。

    那么web服務器有什么用呢?web服務器一個優點就是在處理靜態信息上。例如一些靜態的html,圖片,等等其他靜態的東西。

    那為什么tomcat不能具備這些優點?這個問題我們可以換一個說法:為什么會計不能做市場營銷呢?

    所以嘛,大家要分工明確,應用服務器就做好它該做的:如何解釋一個jsp,如何處理java文件等等,做好這一點就足夠了。而web服務器也做好它該做的:如何快速向瀏覽器傳遞信息,如何快速地讓瀏覽器下載圖片。

    那你又問了,那為啥tomcat還提供一個http服務?那不是讓你開發方便嘛!千萬別把tomcat的http服務當成是一個web服務器。

    說了這么多,那么我們對應用服務器和web服務器的整合也應該心里有數了。就拿tomcat和iis整合來說事吧!

    我們到底想干什么呢?很明顯,我們想讓tomcat 處理對 java應用的請求,而iis應該處理圖片,css 等等其他靜態資源的事情。

    具體的細節不談了,無非就是配置 ispai_redirect 這個東東。因為我們主要說的分工問題,所以還是說說這個 uriworkermap.properties 文件。

    這個文件就是處理分工的用的。例如我定義成如下這個樣子:
    /www.5a520.cn /eshop/*.do=ajp13
    /www.5a520.cn /eshop/dwr/interface/*=ajp13
    /www.5a520.cn /eshop/dwr/*=ajp13
    /www.bt285.cn /eshop/js/*=ajp13

    那么就告訴了 isapi_redirect , 以上4種請求,都交給tomcat處理。
    那么其他的請求呢?當然是交給 iis了。

    如果我定義成這個樣子:
    /* = ajp13

    這下可慘了,iis被你浪費了,就好像你招聘了一個會計和一個推銷的人員,但是讓會計干財務的活之外,還干了推銷。而推銷人員給閑置了。

    至于 uriworkermap.properties  的詳細配置,可以參考 tomcat 網站,上面有詳細的講解。


    兩種服務器的整合雖然不難,但是如果不明白其中的意義和原理,一旦項目配置有所變化,那就是沒有葫蘆就畫不出來瓢了。
    posted @ 2008-10-08 21:17 bt下載| 編輯 收藏

    mysql slow log 是用來記錄執行時間較長(超過long_query_time秒)的sql的一種日志工具.

    啟用 slow log

    有兩種啟用方式:
    1, 在my.cnf 里 通過 log-slow-queries[=file_name]
    2, 在mysqld進程啟動時,指定--log-slow-queries[=file_name]選項

    比較的五款常用工具

    mysqldumpslow, mysqlsla, myprofi, mysql-explain-slow-log, mysqllogfilter


    mysqldumpslow, mysql官方提供的慢查詢日志分析工具. 輸出圖表如下:
    主要功能是, 統計不同慢sql的
    出現次數(Count), 
    執行最長時間(Time), 
    累計總耗費時間(Time), 
    等待鎖的時間(Lock), 
    發送給客戶端的行總數(Rows), 
    掃描的行總數(Rows), 
    用戶以及sql語句本身(抽象了一下格式, 比如 limit 1, 20 用 limit N,N 表示).

    mysqlsla, hackmysql.com推出的一款日志分析工具(該網站還維護了 mysqlreport, mysqlidxchk 等比較實用的mysql工具)
    整體來說, 功能非常強大. 數據報表,非常有利于分析慢查詢的原因, 包括執行頻率, 數據量, 查詢消耗等.

    格式說明如下:
    總查詢次數 (queries total), 去重后的sql數量 (unique)
    輸出報表的內容排序(sorted by)
    最重大的慢sql統計信息, 包括 平均執行時間, 等待鎖時間, 結果行的總數, 掃描的行總數.

    Count, sql的執行次數及占總的slow log數量的百分比.
    Time, 執行時間, 包括總時間, 平均時間, 最小, 最大時間, 時間占到總慢sql時間的百分比.
    95% of Time, 去除最快和最慢的sql, 覆蓋率占95%的sql的執行時間.
    Lock Time, 等待鎖的時間.
    95% of Lock , 95%的慢sql等待鎖時間.
    Rows sent, 結果行統計數量, 包括平均, 最小, 最大數量.
    Rows examined, 掃描的行數量.
    Database, 屬于哪個數據庫
    Users, 哪個用戶,IP, 占到所有用戶執行的sql百分比

    Query abstract, 抽象后的sql語句
    Query sample, sql語句

    除了以上的輸出, 官方還提供了很多定制化參數, 是一款不可多得的好工具.

    mysql-explain-slow-log, 德國人寫的一個perl腳本.
    http://www.willamowius.de/mysql-tools.html

    功能上有點瑕疵, 不僅把所有的 slow log 打印到屏幕上, 而且統計也只有數量而已. 不推薦使用.
    mysql-log-filter, google code上找到的一個分析工具.提供了 python 和 php 兩種可執行的腳本.
    http://code.google.com/p/mysql-log-filter/
    功能上比官方的mysqldumpslow, 多了查詢時間的統計信息(平均,最大, 累計), 其他功能都與 mysqldumpslow類似.
    特色功能除了統計信息外, 還針對輸出內容做了排版和格式化, 保證整體輸出的簡潔. 喜歡簡潔報表的朋友, 推薦使用一下.
    myprofi, 純php寫的一個開源分析工具.項目在 sourceforge 上.
    http://myprofi.sourceforge.net/

    功能上, 列出了總的慢查詢次數和類型, 去重后的sql語句, 執行次數及其占總的slow log數量的百分比.
    從整體輸出樣式來看, 比mysql-log-filter還要簡潔. 省去了很多不必要的內容. 對于只想看sql語句及執行次數的用戶來說, 比較推薦.

    總結

    工具/功能 一般統計信息 高級統計信息 腳本 優勢
    mysqldumpslow 支持 不支持 perl mysql官方自帶
    mysqlsla 支持 支持 perl 功能強大,數據報表齊全,定制化能力強.
    mysql-explain-slow-log 支持 不支持 perl
    mysql-log-filter 支持 部分支持 python or php 不失功能的前提下,保持輸出簡潔
    myprofi 支持 不支持 php 非常精簡
    posted @ 2008-10-07 23:33 bt下載| 編輯 收藏

    一、Java編碼是怎么回事?

    對于使用中文以及其他非拉丁語系語言的開發人員來說,經常會遇到字符集編碼問題。對于Java語言來說,在其內部使用的是UCS2編碼(2個字節的Unicode編碼)。這種編碼并不屬于某個語系的語言編碼,它實際上是一種編碼格式的世界語。在這個世界上所有可以在計算機中使用的語言都有對應的UCS2編碼。

    正是因為Java采用了UCS2,因此,在Java中可以使用世界上任何國家的語言來為變量名、方法名、類起名,如下面代碼如下:


    class 中國
    {
        
    public String 雄起()
        {
             
    return "中國雄起";
        }
    }

    中國 祖國 
    = new 中國();
    System.out.println(祖國.雄起());

        哈哈,是不是有點象“中文編程”。實際上,也可以使用其他的語言來編程,如下面用韓文和日文來定義個類:

    class ???
    {
        
    public void スーパーマン() {  }
    }

        實際上,由于Java內部使用的是UCS2編碼格式,因為,Java并不關心所使用的是哪種語言,而只要這種語言在UCS2中有定義就可以。

        UCS2編碼中為不同國家的語言進行了分頁,這個分頁也叫“代碼頁”或“編碼頁”。中文根據包含中文字符的多少,分了很多代碼頁,如cp935cp936等,然而,這些都是在UCS2中的代碼頁名,而對于操作系統來說,如微軟的windows,一開始的中文編碼為GB2312,后來擴展成了GBK。其實GBKcp936是完全等效的,用它們哪個都行。

    二、Java編碼轉換

       
    上面說了這么多,在這一部分我們做一些編碼轉換,看看會發生什么事情。

        先定義一個字符串變量:

        String gbk = "
    中國"; // “中國”在Java內部是以UCS2格式保存的

        用下面的語言輸出一定會輸出中文:

    System.out.println(gbk);

        實現上,當我們從IDE輸入“中國”時,用的是java源代碼文件保存的格式,一般是GBK,有時也可是utf-8,而在Java編譯程序時,會不由分說地將所有的編碼格式轉換成utf-8編碼,讀者可以用UltraEdit或其他的二進制編輯器打開上面的“中國.class”,看看所生成的二進制是否有utf-8的編碼(utf-8ucs2之間的轉換非常容易,因為utf-8ucs2之間是用公式進行轉換的,而不是到代碼頁去查,這就相當于將二進制轉成16進制一樣,4個字節一組)。如“中國”的utf-8編碼按著GBK解析就是“涓  浗”。如下圖所示。



    如果使用下面的語言可以獲得“中國”的utf-8字節,結果是6(一個漢字由3個字節組成)

    System.out.println(gbk.getBytes("utf-8").length);

    下面的代碼將輸出“涓  浗”。

    System.out.println(new String(gbk.getBytes("utf-8"), "gbk"));   

    由于將“中國“的utf-8編碼格式按著gbk解析,所以會出現亂碼。

    如果要返回中文的UCS2編碼,可以使用下面的代碼:

    System.out.println(gbk.getBytes("unicode")[2]);

    System.out.println(gbk.getBytes("unicode")[3]);

    前兩個字節是標識位,要從第3個字節開始。還有就是其他的語言使用的編碼的字節順序可能不同,如在C#中可以使用下面的代碼獲得“中國“的UCS2編碼:

    String s = "
    ";

    MessageBox.Show(ASCIIEncoding.Unicode.GetBytes(s)[0].ToString());

    MessageBox.Show(ASCIIEncoding.Unicode.GetBytes(s)[1].ToString());

        使用上面的java代碼獲得的“中“的16進制UCS2編碼為4E2D,而使用C#獲得的相應的ucs2編碼為2D4E,這只是C#Java編碼內部使用的問題,并沒有什么關系。但在C#Java互操作時要注意這一點。

        如果使用下面的java編碼將獲得16進制的“中”的GBK編碼:

    System.out.println(Integer.toHexString(0xff & xyz.getBytes("gbk")[0]));

    System.out.println(Integer.toHexString(0xff & xyz.getBytes("gbk")[1]));

    “中”的ucs2編碼為2D4E,GBK編碼為D6D0

        讀者可訪問如下的url自行查驗:

        http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT

        當然,感興趣的讀者也可以試試其他語言的編碼,如“人類”的韓語是“???”,如下面的代碼將輸出“???”的cp949ucs2編碼,其中cp949是韓語的代碼頁。


    String korean = "???"// 共三個韓文字符,我們只測試第一個“?”

    System.out.println(Integer.toHexString(
    0xff & korean.getBytes("unicode")[2]));

    System.out.println(Integer.toHexString(
    0xff & korean.getBytes("unicode")[3]));

    System.out.println(Integer.toHexString(
    0xff & korean.getBytes("Cp949")[0]));

    System.out.println(Integer.toHexString(
    0xff & korean.getBytes("Cp949")[1]));

     

    上面代碼的輸出結果如下:

    c7

    78

    c0

    ce

        也就是說“?”的ucs2編碼為C778,cp949的編碼為C0CE,要注意的是,在cp949中,ucs2編碼也有C0CE,不要弄混了。讀者可以訪問下面的url來驗證:

    http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP949.TXT

    http://www.bt285.cn/content.php?id=1196863

    Java支持的編碼格式

    三、屬性文件

    Java中的屬性文件只支持iso-8859-1編碼格式,因此,要想在屬性文件中保存中文,就必須使用UCS2編碼格式("uxxxx),因此,出現了很多將這種編碼轉換成可視編碼和工具,如Eclipse中的一些屬性文件編輯插件。

    實際上,"uxxxx編碼格式在javaC#中都可以使用,如下面的語句所示:

    String name= ""u7528"u6237"u540d"u4e0d"u80fd"u4e3a"u7a7a" ;

    System.out.println(name);

        上面代碼將輸出“用戶名不能為空”的信息。將"uxxxx格式顯示成中文非常簡單,那么如何將中文還原成"uxxxxx格式呢?下面的代碼完成了這個工作:


    String ss = "用戶名不能為空";
    byte[] uncode = ss.getBytes("Unicode");
    int x = 0xff;
    String result 
    ="";
    for(int i= 2; i < uncode.length; i++)
    {
        
    if(i % 2 == 0) result += "\\u";
        String abc 
    = Integer.toHexString(x & uncode[i]);            
        result 
    += abc.format("%2s", abc).replaceAll(" ""0");               
    }
    System.out.println(result);

     

        上面的代碼將輸出如下結果:


    \u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a

        好了,現在可以利用這個技術來實現一個屬性文件編輯器了。

    四、Web中的編碼問題

        大家碰到最多的編碼問題就是在Web應用中。先讓我們看看下面的程序:

     

    <!--  main.jsp  -->

      
    <%@ page language="java"  pageEncoding="utf-8"%>

      
    <html>
          
    <head>

          
    </head>

          
    <body>
              
    <form action="servlet/MyPost" method="post">
                  
    <input type="text" name="user" />
                  
    <p/>
                  
    <input type="submit"  value="提交"/>
              
    </form>

          
    </body>
      
    </html>


        下面是個Servlet

      package servlet;

      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;

      
    public class MyPost extends HttpServlet
      {

          
    public void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException
          {
              String user 
    = request.getParameter("user");
              System.
    out.println(user);
          }
      }


        如果中main.jsp中輸入中文后,向MyPost提交,在控制臺中會輸出“中å?½”,一看就是亂碼。如果將IE的當前編碼設成其他的,如由utf-8改為gbk,仍然會出現亂碼,只是亂得不一樣而已。這是因為客戶端提交數據時是根據瀏覽器當前的編碼格式來提交的,如瀏覽器當前為gbk編碼,就以gbk編碼格式來提交。 這本身是不會出現亂碼的,問題就出在Web服務器接收數據的時候,HttpServletRequest在將客戶端傳來的數據轉成ucs2上出了問題。在默認情況下,是按著iso-8859-1編碼格式來轉的,而這種編碼格式并不支持中文,所以也就無法正常顯示中文了,解決這個問題的方法是用和客戶端瀏覽器當前編碼格式一致的編碼來轉換,如果是utf-8,則在doPost方法中應該用以下的語句來處理:

        request.setCharacterEncoding("utf-8");

        為了對每一個Servlet都起作用,可以將上面的語句加到filter里。

        另外,我們一般使用象MyEclipse一樣的IDE來編寫jsp文件,這樣的工具會根據pageEncoding屬性將jsp文件保存成相應的編碼格式,但如果要使用象記事本一樣的簡單的編輯器來編寫jsp文件,如果pageEncodingutf-8,而在默認時,記事本會將文件保存成iso-8859-1ascii)格式,但在myeclipse里,如果文件中有中文,它是不允許我們保存成不支持中文的編碼格式的,但記事本并不認識jsp,因此,這時在ie中就無法正確顯示出中文了。除非用記事本將其保存在utf-8格式。如下圖:


     

    http://blog.csdn.net/wangdei/archive/2008/10/07/3030758.aspx
    posted @ 2008-10-07 22:36 bt下載 閱讀(513) | 評論 (0)編輯 收藏

         摘要: 在沒有使用Spring提供的Open Session In View情況下,因需要在service(or Dao)層里把session關閉,所以lazy loading 為true的話,要在應用層內把關系集合都初始化,如 company.getEmployees(),否則Hibernate拋session already closed Exception;    Op...  閱讀全文
    posted @ 2008-10-05 15:01 bt下載| 編輯 收藏

    本文以http://www.bt285.cn 網站為例,為剛做完第一階段的性能優化作一個總結,希望能給大家拋磚引玉。
    我的這個網站剛上線不久,因為時間比較倉促,所以先發布,再來優化性能。經過優化后從數據看是非常明顯的,不過你訪問的時候可能還是比較慢,因為目前我的服務器帶寬只有電信2M獨享,呵呵,見笑了吧!窮,沒辦法。這個網站主要以分享圖片為主題,功能比較簡單。將來也不會做的復雜,因為你從名字中就可以看出 eaful , easy is beautiful. 呵呵。

    我的性能優化分兩個階段實現:
    第一階段:前臺性能優化
    主要指減小頁面輸出流,完善客戶端cache策略。
    第二階段:后臺性能優化
    主要包括數據庫優化,cache優化,算法優化和Page Stream Cache。
    目前完成了第一階段,第二階段估計在10月國慶節完成。下面詳細介紹第一階段的性能優化。

    1)為靜態文件獨立域名
    因為我的網站主要是圖片,每張圖片就有50-60K,以后訪問量大的時候會獨立一臺圖片服務器,以減輕應用服務器的壓力,所以為靜態文件的URL獨立一個二級域名(bt.bt285.cn),為什么不獨立一個頂級域名,因為考慮到cookie共享等因素,比如有些圖片必須登陸后訪問。

    2)優化客戶端cache
    比如一些靜態文件,如圖片,css文件,js文件等不用每次都更新,所以使用永久cache。方法是在response頭中設置Cache-Control 為max-age=99999999,大概緩存3年左右吧。如果css文件修改了怎么辦呢,所以這里的關鍵點是在URL中放入版本參數。如:http://www.bt285.cn/content.php?id=1196863 v1222319367387其中v1222319367387是版本信息,當文件修改后,就修改這個版本號輸出鏈接。這樣瀏覽器發現這是一個新URL,所以會重新請求。
    這里要注意的是只對有含有版本號的URL緩存,否則一旦客戶端永久cache你就不能讓他何時更新了。如更改了js文件,客戶端沒有更新,這樣可能報錯了你也不知道。
    對圖片的鏈接,就算不包含版本號,也至少讓他緩存到凌晨過期(使用Expires頭)。這樣不會每次刷新頁面都更新圖片。而且就算圖片有修改,最多也是晚一天看到罷了。

    3)合并css,js文件
    我本來有3個css文件,5個js文件。首次打開的時候要請求8次,雖然現代瀏覽器支持長連接,但還是沒有放到一個文件中快,只要下載一次就夠了。我把css文件合并為一個,js文件合并為一個,效果很明顯。我這里使用的合并不是發布期手工合并,而是運行期根據配置合并,在應用啟動時合并載入在內存中。
    謝謝quake wang的提醒:把css中的小圖片合成一張大圖片,然后在css中調節顯示位置。但這里需要注意的是修改圖片時,要保持原圖片的位置不變,并且不影響其他圖片的位置。我目前的做法是給每個小圖片一定的空余空間,如一個30*30px的圖片,我給它在大圖中的空間是50×50px,這樣的話以后這個圖片需要變大的話,不會影響右邊和下邊的圖片了。如果超過50×50px,就給它100×100px,反正是某個整數的倍數,這樣便于在css中調節顯示位置。

    4)壓縮文本輸出
    css文件,js文件,html文件都是文本輸出,都可以用gzip壓縮,而且有立竿見影的效果。我的200K的js文件壓縮后只有49K了,還是很滿意的。也可以考慮在壓縮前用ShrinkSafe先瘦身,它可以把注釋去掉,縮短變量名的方法減小js文件,以我200K的js文件為例,瘦身后只有 100K了,但是用gzip壓縮,只能壓到39K,感覺效果不是很明顯,而且據說有eval的bug,所以我這里沒有采用。還可以考慮對css文件, html文件進行瘦身,比如刪除兩邊的空格,回車等,但我覺的使用了gzip壓縮,刪除和不刪除空格,壓縮效果應該不會相差太大,所以也沒有采用。

    以下是我在firefox中使用firebug測試性能的截圖,優化前和優化后的效果比較。

    優化前:


    優化后:


    以上是firfox首次打開http://www.bt285.cn/content.php?id=1196863的截圖,整整提高了1秒多。第二次打開就更快了,幾乎所有東西靜態文件都在客戶端有緩存。

    也可以使用http://www.bt285.cn/1196863/pic/ 甜性澀愛 來分析頁面下載性能。
    優化前:
    Object Size Totals
    Object type Size (bytes) Download @ 56K (seconds) Download @ T1 (seconds)
    HTML: 46442 9.46 0.45
    HTML Images: 10354 2.46 0.45
    CSS Images: 19620 6.31 2.50
    Total Images: 29974 8.77 2.95
    Javascript: 186931 38.66 2.39
    CSS: 16635 4.12 0.89
    Multimedia: 0 0.00 0.00
    Other: 0 0.00 0.00

    優化后:
    Object Size Totals
    Object type Size (bytes) Download @ 56K (seconds) Download @ T1 (seconds)
    HTML: 45412 9.25 0.44
    HTML Images: 10354 2.46 0.45
    CSS Images: 0 0.00 0.00
    Total Images: 10354 2.46 0.45
    Javascript: 47708 9.71 0.45
    CSS: 4028 1.00 0.22
    Multimedia: 0 0.00 0.00
    Other: 0 0.00 0.00

    優化前和優化后的效果還是很明顯的,但是js文件中使用了prototype.js這個js文件,有126K,以后有時間的話會把這個文件替換掉。
    posted @ 2008-10-01 15:40 bt下載| 編輯 收藏

    僅列出標題
    共2頁: 上一頁 1 2 
    主站蜘蛛池模板: 波多野结衣视频在线免费观看| 亚洲日产韩国一二三四区| 免费中文字幕视频| 亚洲欧洲日产国码无码久久99| 鲁大师在线影院免费观看| 亚洲欧洲av综合色无码| 国产日产亚洲系列| 成年私人影院免费视频网站| a在线视频免费观看在线视频三区| 无码久久精品国产亚洲Av影片| 成全视频免费高清 | 国产精品视频全国免费观看 | 国产亚洲男人的天堂在线观看| 成人午夜亚洲精品无码网站| 青娱乐免费视频在线观看| 久青草国产免费观看| 亚洲人成电影青青在线播放| 久久久久无码专区亚洲av| 免费看国产成年无码AV片| 热99RE久久精品这里都是精品免费| 久久综合久久综合亚洲| 国产精品亚洲成在人线| 国产网站免费观看| 97视频热人人精品免费| 成人无码视频97免费| 亚洲av日韩综合一区久热| 亚洲精品电影天堂网| 亚洲精品午夜国产VA久久成人 | 免费a级黄色毛片| www.免费在线观看| 成人爽a毛片免费| 污视频网站免费观看| 中文字幕无码亚洲欧洲日韩| 亚洲av无码无在线观看红杏| 四虎在线播放免费永久视频| 成年在线观看免费人视频草莓| 亚洲精品免费在线观看| 中文毛片无遮挡高清免费| 男女超爽视频免费播放| 亚洲乱色熟女一区二区三区蜜臀| 亚洲视频在线观看网站|