JAVA 双向SSL,SOCKET客户端/服务端

实现技术:
JSSE(Java Security Socket Extension)
Server需要:
1)KeyStore: 其中保存服务端的私钥
2)Trust KeyStore:其中保存客户端的授权证书
Client需要:
1)KeyStore:其中保存客户端的私钥
2)Trust KeyStore:其中保存服务端的授权证书

使用Java自带的keytool命令,去生成这样信息文件:

1)生成服务端私钥,并且导入到服务端KeyStore文件中

2)根据私钥,导出服务端证书

3)将服务端证书,导入到客户端的Trust KeyStore中

采用同样的方法,生成客户端的私钥,客户端的证书,并且导入到服务端的Trust KeyStore中
1)keytool -genkey -alias clientkey -keystore kclient.keystore
2)keytool -export -alias clientkey -keystore kclient.keystore -file client.crt
3)keytool -import -alias clientkey -file client.crt -keystore tserver.keystore

Server:
Java代码
public class Server implements Runnable{   
  
    private static final int     DEFAULT_PORT                     = 7777;   
  
    private static final String SERVER_KEY_STORE_PASSWORD        = "123456";   
    private static final String SERVER_TRUST_KEY_STORE_PASSWORD = "123456";   
  
    private SSLServerSocket      serverSocket;   
  
    /**
      * 启动程序
      *
      * @param args
      */  
    public static void main(String[] args) {   
         Server server = new Server();   
         server.init();   
         Thread thread = new Thread(server);   
         thread.start();   
     }   
  
    public synchronized void start() {   
        if (serverSocket == null) {   
             System.out.println("ERROR");   
            return;   
         }   
        while (true) {   
            try {   
                 Socket s = serverSocket.accept();   
                 InputStream input = s.getInputStream();   
                 OutputStream output = s.getOutputStream();   
  
                 BufferedInputStream bis = new BufferedInputStream(input);   
                 BufferedOutputStream bos = new BufferedOutputStream(output);   
  
                byte[] buffer = new byte[20];   
                 bis.read(buffer);   
                 System.out.println("——receive:——–"+new String(buffer).toString());   
  
                 bos.write("yes".getBytes());   
                 bos.flush();   
  
                 s.close();   
             } catch (Exception e) {   
                 System.out.println(e);   
             }   
         }   
     }   
    public void init() {   
        try {   
             SSLContext ctx = SSLContext.getInstance("SSL");   
  
             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");   
             TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");   
  
             KeyStore ks = KeyStore.getInstance("JKS");   
             KeyStore tks = KeyStore.getInstance("JKS");   
  
             ks.load(new FileInputStream("src/ssl/kserver.keystore"), SERVER_KEY_STORE_PASSWORD.toCharArray());   
             tks.load(new FileInputStream("src/ssl/tserver.keystore"), SERVER_TRUST_KEY_STORE_PASSWORD.toCharArray());   
  
             kmf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());   
             tmf.init(tks);   
  
             ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);   
  
             serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(DEFAULT_PORT);   
             serverSocket.setNeedClientAuth(true);   
         } catch (Exception e) {   
             System.out.println(e);   
         }   
     }   
  
    public void run() {   
        // TODO Auto-generated method stub   
         start();   
     }   
}  

Client:
Java代码
package ssl;   
  
import java.io.BufferedInputStream;   
import java.io.BufferedOutputStream;   
import java.io.FileInputStream;   
import java.io.IOException;   
import java.io.InputStream;   
import java.io.OutputStream;   
import java.security.KeyStore;   
  
import javax.net.ssl.KeyManagerFactory;   
import javax.net.ssl.SSLContext;   
import javax.net.ssl.SSLSocket;   
import javax.net.ssl.TrustManagerFactory;   
  
/**
* SSL Client
*
* @author Leo
*/  
public class Client {   
  
    private static final String DEFAULT_HOST                     = "127.0.0.1";   
    private static final int     DEFAULT_PORT                     = 7777;   
  
    private static final String CLIENT_KEY_STORE_PASSWORD        = "123456";   
    private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456";   
  
    private SSLSocket            sslSocket;   
  
    /**
      * 启动客户端程序
      *
      * @param args
      */  
    public static void main(String[] args) {   
        Client client = new Client();   
         client.init();   
         client.process();   
     }   
  
  
    public void process() {   
        if (sslSocket == null) {   
             System.out.println("ERROR");   
            return;   
         }   
        try {   
             InputStream input = sslSocket.getInputStream();   
             OutputStream output = sslSocket.getOutputStream();   
  
             BufferedInputStream bis = new BufferedInputStream(input);   
             BufferedOutputStream bos = new BufferedOutputStream(output);   
  
             bos.write("1234567890".getBytes());   
             bos.flush();   
  
            byte[] buffer = new byte[20];   
             bis.read(buffer);   
             System.out.println(new String(buffer));   
  
             sslSocket.close();   
         } catch (IOException e) {   
             System.out.println(e);   
         }   
     }   
  
  
    public void init() {   
        try {   
             SSLContext ctx = SSLContext.getInstance("SSL");   
  
             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");   
             TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");   
  
             KeyStore ks = KeyStore.getInstance("JKS");   
             KeyStore tks = KeyStore.getInstance("JKS");   
  
             ks.load(new FileInputStream("src/ssl/kclient.keystore"), CLIENT_KEY_STORE_PASSWORD.toCharArray());   
             tks.load(new FileInputStream("src/ssl/tclient.keystore"), CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());   
  
             kmf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());   
             tmf.init(tks);   
  
             ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);   
  
             sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket(DEFAULT_HOST, DEFAULT_PORT);   
         } catch (Exception e) {   
             System.out.println(e);   
         }   
     }   
  
}  

启动Server

启动Client,发送信息。

Server接收如下:正确解密
返回Client信息,

如此,就完成了服务端和客户端之间的基于身份认证的交互。

client采用kclient.keystore中的clientkey私钥进行数据加密,发送给server。
server采用tserver.keystore中的client.crt证书(包含了clientkey的公钥)对数据解密,如果解密成功,证明消息来自client,进行逻辑处理。

server采用kserver.keystore中的serverkey私钥进行数据加密,发送给client。
client采用tclient.keystore中的server.crt证书(包含了serverkey的公钥)对数据解密,如果解密成功,证明消息来自server,进行逻辑处理。

如果过程中,解密失败,那么证明消息来源错误。不进行逻辑处理。这样就完成了双向的身份认证。

keytools简介

keytool JAVA是个密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据 完整性以及认证服务。它还允许用户储存他们的通信对等者的公钥(以证书形式)。通过keytool –help查看其用法;

创建证书Java 中的 keytool.exe (位于 JDK/Bin 目录下)可以用来创建数字证书,所有的数字证书是以一条一条(采用别名区别)的形式存入证书库的中,证书库中的一条证书包含该条证书的私钥,公钥和对应的 数字证书的信息。证书库中的一条证书可以导出数字证书文件,数字证书文件只包括主体信息和对应的公钥。
每一个证书库是一个文件组成,它有访问密码,在首次创建时,它会自动生成证书库,并要求指定访问证书库的密码。
在创建证书的的时候,需要填写证书的一些信息和证书对应的私钥密码。这些信息包括 CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx,它们的意思是:
Ø CN(Common Name – 名字与姓氏):其实这个“名字与姓氏”应该是域名,比如说localhost或是blog.devep.net之类的。输成了姓名,和真正运行的时候域名 不符,会出问题。浏览器访问时,弹出一个对话框,提示“安全证书上的名称无效,或者与站点名称不匹配”,用户选择继续还是可以浏览网页。但是用http client写程序访问的时候,会抛出类似于“javax.servlet.ServletException: HTTPS hostname wrong: should be ”的异常。
Ø OU(Organization Unit – 组织单位名称)
Ø O(Organization – 组织名称)
Ø L(Locality – 城市或区域名称)
Ø ST(State – 州或省份名称)
Ø C(Country – 国家名称)
可以采用交互式让工具提示输入以上信息,也可以采用参数,如:-dname “CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx”来自动创建。

创建一个证书
指定证书库为 D:/keystore/test,创建别名为 Tomcat 的一条证书,它指定用 RSA 算法生成,且指定密钥长度为 1024,证书有效期为 1 年:
keytool -genkey -alias Tomcat -keyalg RSA -keysize 1024 -keystore C:/keystore/test -validity 365
显示证书库中的证书使用如下命令: keytool -list -keystore C:/keystore/test 将显示 C:/keystore/test 证书库的的所有证书列表

导出到证书文件
使用命令:keytool -export -alias Tomcat -file C:/keystore/TC.cer -keystore C:/keystore/test 将把证书库C:/keystore/test 中的别名为 Tomcat 的证书导出到 TC.cer 证书文件中,它包含证书主体的信息及证书的公钥,不包括私钥,可以公开。
导出的证书文件是以二进制编码文件,无法用文本编辑器正确显示,可以加上 -rfc参数以一种可打印的编者编码输出。 如:
keytool -export -alias Tomcat -file C:/keystore/TC.cer -keystore C:/keystore/test –rfc

查看证书的信息
通过命令: keytool -printcert -file D:/keystore/TC.cer 可以查看证书文件的信息。 也可以在 Windows 资源管理器中双击产生的证书文件直接查看。

删除密钥库中的条目

keytool -delete -alias Tomcat -keystore C:/keystore/test
这条命令将 C:/keystore/test 库中的 Tomcat 这一条证书删除了。

修改证书条目口令
keytool -keypasswd -alias Tomcat -keystore C:/keystore/test,可以以交互的方式修改 C:/keystore/test 证书库中的条目为 Tomcat 的证书。
Keytool -keypasswd -alias Tomcat -keypass oldpasswd -new newpasswd -storepass storepasswd -keystore C:/keystore/test

这一行命令以非交互式的方式修改库中别名为 Tomcat 的证书的密码为新密码 newpasswd,行中的 oldpasswd 是指该条证书的原密码, storepasswd 是指证书库的密码。

德兰修女语录

人们经常是不讲道理的、没有逻辑的和以自我为中心的
不管怎样,你要原谅他们

即使你是友善的,人们可能还是会说你自私和动机不良
不管怎样,你还是要友善

当你功成名就,你会有一些虚假的朋友
和一些真实的敌人
不管怎样,你还是要取得成功
 
即使你是诚实的和率直的,人们可能还是会欺骗你
不管怎样,你还是要诚实和率直

你多年来营造的东西
有人在一夜之间把它摧毁
不管怎样,你还是要去营造
 
如果你找到了平静和幸福,他们可能会嫉妒你
不管怎样,你还是要快乐
 
你今天做的善事,人们往往明天就会忘记
不管怎样,你还是要做善事
 
即使把你最好的东西给了这个世界
也许这些东西永远都不够
不管怎样,把你最好的东西给这个世界

你看,说到底,它是你和上帝之间的事
而决不是你和他人之间的事

家用路由器设置实战

关于如何设置路由器,其实说明书里已经讲的很明白了,这里我只说几个安全配置方面的问题

1、 根据mac地址限制访问

我这里有两台pc,一个虚拟的pc,一个iphone,虚拟的pc是通过桥接的方式上网的,虽然也是从路由器那里动态获取ip地址的,但是,并不需要将虚拟pc的mac地址加入到允许的mac地址列表里,因为走的是桥接模式(什么是桥接模式,我也不清楚了),所以最后允许的mac地址只有三个就可以了,就是两个pc的无线网卡的物理地址,和iphone的wifi的物理地址。

注意:

1. mac地址一定要加在: . 安全设置=》mac地址过滤 里面,而不是: 无线参数=>mac 地址过滤 里面

2. 一定要打开 安全设置=》防火墙设置 里面的 “开启防火墙” 这个总的开关

 

2、端口转发的配置

   如果LAN里的一个机器想提供服务,如:sshd  httpd,那么从
        转发规则=》虚拟服务器  里添加指定的端口转发到提供服务的ip就行了;然后使用该路由器的wan口的ip和配置的port,就可以访问到私网ip的指定服务了;  这就是路由器的nat模式

 

IPSec基础-IPSec服务

IPSec 协议不是一个单独的协议,它给出了应用于IP层上网络数据安全的一整套体系结构,包括网络认证协议 Authentication Header(AH)、封装安全载荷协议Encapsulating Security Payload(ESP)、密钥管理协议Internet Key Exchange (IKE)和用于网络认证及加密的一些算法等。IPSec 规定了如何在对等层之间选择安全协议、确定安全算法和密钥交换,向上提供了访问控制、数据源认证、数据加密等网络安全服务。

  一、安全特性

  IPSec的安全特性主要有:

   ·不可否认性 "不可否认性"可以证实消息发送方是唯一可能的发送者,发送者不能否认发送过消息。"不可否认性"是采用公钥技术的一个特征,当使用公钥技术时,发送方用私钥产生一个数字签名随消息一起发送,接收方用发送者的公钥来验证数字签名。由于在理论上只有发送者才唯一拥有私钥,也只有发送者才可能产生该数字签名,所以只要数字签名通过验证,发送者就不能否认曾发送过该消息。但"不可否认性"不是基于认证的共享密钥技术的特征,因为在基于认证的共享密钥技术中,发送方和接收方掌握相同的密钥。
  ·反重播性 "反重播"确保每个IP包的唯一性,保证信息万一被截取复制后,不能再被重新利用、重新传输回目的地址。该特性可以防止攻击者截取破译信息后,再用相同的信息包冒取非法访问权(即使这种冒取行为发生在数月之后)。
  ·数据完整性 防止传输过程中数据被篡改,确保发出数据和接收数据的一致性。IPSec利用Hash函数为每个数据包产生一个加密检查和,接收方在打开包前先计算检查和,若包遭篡改导致检查和不相符,数据包即被丢弃。
  ·数据可靠性(加密) 在传输前,对数据进行加密,可以保证在传输过程中,即使数据包遭截取,信息也无法被读。该特性在IPSec中为可选项,与IPSec策略的具体设置相关。
  ·认证 数据源发送信任状,由接收方验证信任状的合法性,只有通过认证的系统才可以建立通信连接。

  二、基于电子证书的公钥认证

  一个架构良好的公钥体系,在信任状的传递中不造成任何信息外泄,能解决很多安全问题。IPSec与特定的公钥体系相结合,可以提供基于电子证书的认证。公钥证书认证在Windows 2000中,适用于对非Windows 2000主机、独立主机,非信任域成员的客户机、或者不运行Kerberos v5认证协议的主机进行身份认证。

  三、预置共享密钥认证

  IPSec也可以使用预置共享密钥进行认证。预共享意味着通信双方必须在IPSec策略设置中就共享的密钥达成一致。之后在安全协商过程中,信息在传输前使用共享密钥加密,接收端使用同样的密钥解密,如果接收方能够解密,即被认为可以通过认证。但在Windows 2000 IPSec策略中,这种认证方式被认为不够安全而一般不推荐使用。

  四、公钥加密

  IPSec的公钥加密用于身份认证和密钥交换。公钥加密,也被称为"不对称加密法",即加解密过程需要两把不同的密钥,一把用来产生数字签名和加密数据,另一把用来验证数字签名和对数据进行解密。

  使用公钥加密法,每个用户拥有一个密钥对,其中私钥仅为其个人所知,公钥则可分发给任意需要与之进行加密通信的人。例如:A想要发送加密信息给B,则A需要用B的公钥加密信息,之后只有B才能用他的私钥对该加密信息进行解密。虽然密钥对中两把钥匙彼此相关,但要想从其中一把来推导出另一把,以目前计算机的运算能力来看,这种做法几乎完全不现实。因此,在这种加密法中,公钥可以广为分发,而私钥则需要仔细地妥善保管。

  五、Hash函数和数据完整性

  Hash信息验证码HMAC(Hash message authentication codes)验证接收消息和发送消息的完全一致性(完整性)。这在数据交换中非常关键,尤其当传输媒介如公共网络中不提供安全保证时更显其重要性。

  HMAC结合hash算法和共享密钥提供完整性。Hash散列通常也被当成是数字签名,但这种说法不够准确,两者的区别在于:Hash散列使用共享密钥,而数字签名基于公钥技术。hash算法也称为消息摘要或单向转换。称它为单向转换是因为:

  1)双方必须在通信的两个端头处各自执行Hash函数计算;
  2)使用Hash函数很容易从消息计算出消息摘要,但其逆向反演过程以目前计算机的运算能力几乎不可实现。

  Hash散列本身就是所谓加密检查和或消息完整性编码MIC(Message Integrity Code),通信双方必须各自执行函数计算来验证消息。举例来说,发送方首先使用HMAC算法和共享密钥计算消息检查和,然后将计算结果A封装进数据包中一起发送;接收方再对所接收的消息执行HMAC计算得出结果B,并将B与A进行比较。如果消息在传输中遭篡改致使B与A不一致,接收方丢弃该数据包。

  有两种最常用的hash函数:

 

  ·HMAC-MD5 MD5(消息摘要5)基于RFC1321。MD5对MD4做了改进,计算速度比MD4稍慢,但安全性能得到了进一步改善。MD5在计算中使用了64个32位常数,最终生成一个128位的完整性检查和。
  ·HMAC-SHA 安全Hash算法定义在NIST FIPS 180-1,其算法以MD5为原型。 SHA在计算中使用了79个32位常数,最终产生一个160位完整性检查和。SHA检查和长度比MD5更长,因此安全性也更高。

  六、加密和数据可靠性

  IPSec使用的数据加密算法是DES–Data Encryption Standard(数据加密标准)。DES密钥长度为56位,在形式上是一个64位数。DES以64位(8字节)为分组对数据加密,每64位明文,经过16轮置换生成64位密文,其中每字节有1位用于奇偶校验,所以实际有效密钥长度是56位。 IPSec还支持3DES算法,3DES可提供更高的安全性,但相应地,计算速度更慢。

  七、密钥管理

  ·动态密钥更新

  IPSec策略使用"动态密钥更新"法来决定在一次通信中,新密钥产生的频率。动态密钥指在通信过程中,数据流被划分成一个个"数据块",每一个"数据块"都使用不同的密钥加密,这可以保证万一攻击者中途截取了部分通信数据流和相应的密钥后,也不会危及到所有其余的通信信息的安全。动态密钥更新服务由Internet密钥交换IKE(Internet Key Exchange)提供,详见IKE介绍部分。

  IPSec策略允许专家级用户自定义密钥生命周期。如果该值没有设置,则按缺省时间间隔自动生成新密钥。

  ·密钥长度

  密钥长度每增加一位,可能的密钥数就会增加一倍,相应地,破解密钥的难度也会随之成指数级加大。IPSec策略提供多种加密算法,可生成多种长度不等的密钥,用户可根据不同的安全需求加以选择。

  ·Diffie-Hellman算法

  要启动安全通讯,通信两端必须首先得到相同的共享密钥(主密钥),但共享密钥不能通过网络相互发送,因为这种做法极易泄密。

  Diffie-Hellman算法是用于密钥交换的最早最安全的算法之一。DH算法的基本工作原理是:通信双方公开或半公开交换一些准备用来生成密钥的"材料数据",在彼此交换过密钥生成"材料"后,两端可以各自生成出完全一样的共享密钥。在任何时候,双方都绝不交换真正的密钥。

  通信双方交换的密钥生成"材料",长度不等,"材料"长度越长,所生成的密钥强度也就越高,密钥破译就越困难。 除进行密钥交换外,IPSec还使用DH算法生成所有其他加密密钥。

会自动销毁的script http请求

javascript使用script标签做http请求时,如果不注意script标签的回收的话,可能产生很多垃圾script标签,下面这种方法,可以执行完http请求后自动销毁:

 

function httpScript(_url){
    var 
_head document.getElementsByTagName("head")[0
];
    var 
snode document.createElement("script"
);
    
snode.setAttribute("type""text/javascript"
);
    
snode.setAttribute("language""javascript"
);
    
snode.setAttribute("src"_url
);
    
snode.onload 
= function() {
        
_head.removeChild(snode
);
    } 
    
snode.onreadystatechange = function() {  // 为兼容ie添加的
            if(
snode.readyState == "complete" || snode.readyState == "loaded"
) {
                
_head.removeChild(snode
);
            } 
    }
    
_head.appendChild(snode
);
}

javascript 借助协议从web启动桌面客户端

1. 检查客户端是否存在

2. 启动客户端

下面是一个js的test脚本:

    function IsInstallClient()
    {
        try{
//支持
            
var xmlhttp=new ActiveXObject("Client.test.1"
);
            return 
true
;

        }catch(e){//不支持 
            
return false
;
        }
        return 
false

    }

    function HyperAction(httpstr)
    {
        var 
bRet IsInstallClient
();
        if (
bRet == true
)
        {
            
window.location.href=httpstr
;

        }
        else
        {
            if(confirm("您的浏览器不支持或没有安装客户端,请先安装客户端!\n是否现在下载?"
))
        {
            
window.open("http://client.com.cn/down.html"
);

        }
        }
    }

这个脚本里要注意:

1. xmlhttp=new ActiveXObject("Client.test.1"); 时,浏览器可能会有一个提示,这时 IsInstallClient() 的返回值将是false,这个false是异常块里的false,不是最后的false;

2. 要想避免该提示目前还没有发现什么办法

windows 注册表里注册自己的协议(原创)

im软件但凡要从web直接启动桌面客户端都是通过注册自己的协议来实现的,如果你已经安装了qq,你们 直接在浏览器的地址栏里输入:qq:// 就可以启动qq; 如果你安装了uc,那么直接在浏览器的地址栏里输入: uc:// 就可以直接启动uc了,当然处于安全考虑,可能会有提示的,因为你要从浏览器中跳出来去做其它浏览器无法控制的事情; 所以如果你在开始=>运行 里,输入: qq:// 或 uc:// 就不会提示,而是直接启动qq或uc了,这里也顺便给出了启动qq或uc的另类方法,如果你找不到qq或uc的快捷方式,或者找起来很麻烦,就可以这么搞了。下面还是赶快进入正题吧:

既然qq:// uc://都是一种协议,那么就可以按照http或https来修改了,开始=> 运行=> regedit

然后查找https 全字匹配,只查找“项”(这样会快而且精确),然后将https部分导出成reg文件,内容大致如下:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\https]
@="Safari URL"
"EditFlags"=dword:00000002
"URL Protocol"=""

[HKEY_CLASSES_ROOT\https\DefaultIcon]
@=hex(2):43,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,20,00,46,\
00,69,00,6c,00,65,00,73,00,5c,00,53,00,61,00,66,00,61,00,72,00,69,00,5c,00,\
53,00,61,00,66,00,61,00,72,00,69,00,2e,00,65,00,78,00,65,00,2c,00,31,00,00,\
00

[HKEY_CLASSES_ROOT\https\shell]

[HKEY_CLASSES_ROOT\https\shell\open]

[HKEY_CLASSES_ROOT\https\shell\open\command]
@=hex(2):22,00,43,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,20,\
00,46,00,69,00,6c,00,65,00,73,00,5c,00,53,00,61,00,66,00,61,00,72,00,69,00,\
5c,00,53,00,61,00,66,00,61,00,72,00,69,00,2e,00,65,00,78,00,65,00,22,00,20,\
00,2d,00,75,00,72,00,6c,00,20,00,22,00,25,00,31,00,22,00,00,00

 

编辑reg文件:

去掉不需要的东西,把https替换成shagua(如果你愿意,可以随便起名字),把command部分替换成自己想要执行的应用程序,大致如下:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\shagua]
@="这里随便"
"EditFlags"=dword:00000002
"URL Protocol"=""

[HKEY_CLASSES_ROOT\shagua\shell]

[HKEY_CLASSES_ROOT\shagua\shell\open]

[HKEY_CLASSES_ROOT\shagua\shell\open\command]
@="\"应用程序名\" \"%1\""

保存该文件,双击执行,傻瓜协议就注册完了,在浏览器的地址栏里输入:
shagua://
这时就可以启动你的应用程序了

就这么简单?这只是入门,还有更多,要学会自己研究哦:)

禁止post请求后退、刷新时重新发送post数据

最近有一个需求,就是用户使用post方式登录,然后退出;再点击浏览器的后退按钮回退到发送post请求的那个位置,这时刷新页面,用户名、密码等信息会重新发送,这样,显示用户已登录。下面给出几种避免这种现象的办法:

登录成功后并不直接显示页面,而是:

1. 使用header方式跳转到本页

2. 使用html里的meta来刷新到本页

3. 使用JavaScript的 location.replace() 方法,刷新到本页;注意不能用location.href = …;详见二者的区别

这三种方法本质是一样的,就是让第二次的get请求覆盖上次post请求,这样浏览器的history里面就不会有那次post请求了,用户也就回退不到那次post请求的位置了

 

应该还有更好的办法,但是目前还没有发现,继续探索…