bt下载与小说520

bt下载与小说520

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  16 随笔 :: 0 文章 :: 6 评论 :: 0 Trackbacks

#

第1章基础知识

1.1. 单钥密码体制

单钥密码体制是一种传统的加密算法,是指信息的发送方和接收方共同使用同一把密钥进行加解密。

通常,使用的加密算法比较简便高效,密钥简短,加解密速度快,破译极其困难。但是加密的安全性依靠密钥保管的安全性,在公开的计算机网络上安全地传送和保管密钥是一个严峻的问题,并且如果在多用户的情况下密钥的保管安全性也是一个问题。

单钥密码体制的代表是美国的DES

1.2. 消息摘要

一个消息摘要就是一个数据块的数字指纹。即对一个任意长度的一个数据块进行计算,产生一个唯一指印(对于SHA1是产生一个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算法操作,以保证发信人无法抵赖曾发过该信息(即不可抵赖性),同时也确保信息报文在经签名后末被篡改(即完整性)。当信息接收者收到报文后,就可以用发送者的公钥对数字签名进行验证。 

在数字签名中有重要作用的数字指纹是通过一类特殊的散列函数(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编码为2D4EGBK编码为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编码为C778cp949的编码为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下载 阅读(512) | 评论 (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