項目中有時會用到自動補全查詢,就像Google搜索框、淘寶商品搜索功能,輸入漢字或字母,則以該漢字或字母開頭的相關條目會顯示出來供用戶選擇, autocomplete插件就是完成這樣的功能。
autocomplete官網 : http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/ (可下載jQuery autocomplete插件)。
淘寶商品搜索功能 效果:

下面來使用 autocomplete插件來實現(xiàn)類似效果。
1. 創(chuàng)建 AjaxPage.aspx 頁面,在其中定義 WebMethod 方法來返回 搜索頁面需要的輸入框所有提示條目。 后臺代碼如下:
1 using System.Collections.Generic;
2 using System.IO;
3 using System.Runtime.Serialization.Json;
4 using System.Web.Services;
5
6 public partial class AjaxPage : System.Web.UI.Page
7 {
8 [WebMethod]
9 public static string GetAllHints()
10 {
11 Dictionary<string, string> data = new Dictionary<string, string>();
12 data.Add("蘋果4代iphone正品", "21782");
13 data.Add("蘋果4代 手機套", "238061");
14 data.Add("蘋果4", "838360");
15 data.Add("蘋果皮", "242721");
16 data.Add("蘋果筆記本", "63348");
17 data.Add("蘋果4s", "24030");
18 data.Add("戴爾筆記本", "110105");
19 data.Add("戴爾手機", "18870");
20 data.Add("戴爾鍵盤", "30367");
21
22 DataContractJsonSerializer serializer = new DataContractJsonSerializer(data.GetType());
23
24 using (MemoryStream ms = new MemoryStream())
25 {
26 serializer.WriteObject(ms, data);
27 return System.Text.Encoding.UTF8.GetString(ms.ToArray());
28 }
29 }
30 }
注:該方法返回的數(shù)據(jù)格式為json字符串。
2. 創(chuàng)建搜索頁面 Index.aspx, 前臺代碼如下:

1 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Index.aspx.cs" Inherits="_Default" %>
2
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4
5 <html xmlns="http://www.w3.org/1999/xhtml">
6 <head runat="server">
7 <title></title>
8 <link rel="Stylesheet" href="Styles/jquery.autocomplete.css" />
9 <script type="text/javascript" src="Scripts/jquery-1.4.1.js"></script>
10 <script type="text/javascript" src="Scripts/jquery.autocomplete.js"></script>
11 <script type="text/javascript">
12 var v = 1;
13 $(document).ready(function () {
14 $.ajax({
15 type: "POST",
16 contentType: "application/json",
17 url: "AjaxPage.aspx/GetAllHints",
18 data: "{}",
19 dataType: "json",
20 success: function (msg) {
21 var datas = eval('(' + msg.d + ')');
22 $("#txtIput").autocomplete(datas, {
23 formatItem: function (row, i, max) {
24 return "<table width='400px'><tr><td align='left'>" + row.Key + "</td><td align='right'><font style='color: #009933; font-family: 黑體; font-style: italic'>約" + row.Value + "個寶貝</font> </td></tr></table>";
25 },
26 formatMatch: function(row, i, max){
27 return row.Key;
28 }
29 });
30 }
31 });
32 });
33 </script>
34 </head>
35 <body>
36 <form id="form1" runat="server">
37 <div>
38 <center>
39 <asp:TextBox ID="txtIput" runat="server" Width="400px"></asp:TextBox>
40 </center>
41 </div>
42 </form>
43 </body>
44 </html>

實現(xiàn)效果如下:

3. autocomplete 參數(shù)說明
* minChars (Number)
在觸發(fā)autoComplete前用戶至少需要輸入的字符數(shù).Default: 1,如果設為0,在輸入框內雙擊或者刪除輸入框內內容時顯示列表
* width (Number)
指定下拉框的寬度. Default: input元素的寬度
* max (Number)
autoComplete下拉顯示項目的個數(shù).Default: 10
* delay (Number)
擊鍵后激活autoComplete的延遲時間(單位毫秒).Default: 遠程為400 本地10
* autoFill (Boolean)
要不要在用戶選擇時自動將用戶當前鼠標所在的值填入到input框. Default: false
* mustMatch (Booolean)
如果設置為true,autoComplete只會允許匹配的結果出現(xiàn)在輸入框,所有當用戶輸入的是非法字符時將會得不到下拉框.Default: false
* matchContains (Boolean)
決定比較時是否要在字符串內部查看匹配,如ba是否與foo bar中的ba匹配.使用緩存時比較重要.不要和autofill混用.Default: false
* selectFirst (Boolean)
如果設置成true,在用戶鍵入tab或return鍵時autoComplete下拉列表的第一個值將被自動選擇,盡管它沒被手工選中(用鍵盤或鼠標).當然如果用戶選中某個項目,那么就用用戶選中的值. Default: true
* cacheLength (Number)
緩存的長度.即對從數(shù)據(jù)庫中取到的結果集要緩存多少條記錄.設成1為不緩存.Default: 10
* matchSubset (Boolean)
autoComplete可不可以使用對服務器查詢的緩存,如果緩存對foo的查詢結果,那么如果用戶輸入foo就不需要再進行檢索了,直接使用緩存.通常是打開這個選項以減輕服務器的負擔以提高性能.只會在緩存長度大于1時有效.Default: true
* matchCase (Boolean)
比較是否開啟大小寫敏感開關.使用緩存時比較重要.如果你理解上一個選項,這個也就不難理解,就好比foot要不要到FOO的緩存中去找.Default: false
* multiple (Boolean)
是否允許輸入多個值即多次使用autoComplete以輸入多個值. Default: false
* multipleSeparator (String)
如果是多選時,用來分開各個選擇的字符. Default: ","
* scroll (Boolean)
當結果集大于默認高度時是否使用卷軸顯示 Default: true
* scrollHeight (Number)
自動完成提示的卷軸高度用像素大小表示 Default: 180
* formatItem (Function)
為每個要顯示的項目使用高級標簽.即對結果中的每一行都會調用這個函數(shù),返回值將用LI元素包含顯示在下拉列表中. Autocompleter會提供三個參數(shù)(row, i, max): 返回的結果數(shù)組, 當前處理的行數(shù)(即第幾個項目,是從1開始的自然數(shù)), 當前結果數(shù)組元素的個數(shù)即項目的個數(shù). Default: none, 表示不指定自定義的處理函數(shù),這樣下拉列表中的每一行只包含一個值.
* formatResult (Function)
和formatItem類似,但可以將將要輸入到input文本框內的值進行格式化.同樣有三個參數(shù),和formatItem一樣.Default: none,表示要么是只有數(shù)據(jù),要么是使用formatItem提供的值.
* formatMatch (Function)
對每一行數(shù)據(jù)使用此函數(shù)格式化需要查詢的數(shù)據(jù)格式. 返回值是給內部搜索算法使用的. 參數(shù)值row
* extraParams (Object)
為后臺(一般是服務端的腳本)提供更多的參數(shù).和通常的作法一樣是使用一個鍵值對對象.如果傳過去的值是{ bar:4 },將會被autocompleter解析成my_autocomplete_backend.php?q=foo&bar=4 (假設當前用戶輸入了foo). Default: {}
* result (handler)
此事件會在用戶選中某一項后觸發(fā),參數(shù)為:
event: 事件對象. event.type為result.
data: 選中的數(shù)據(jù)行.
formatted:formatResult函數(shù)返回的值
例如:
$("#singleBirdRemote").result(function(event, data, formatted) {
//如選擇后給其他控件賦值,觸發(fā)別的事件等等
});
過頁頭生成Token,進行請求驗證,解決Ajax請求安全問題。目前為止我做的最多的防止ajax請求攻擊的就是添加驗證碼、添加隨機Token,限制同一請求在規(guī)定時間內的最大請求數(shù)。
下面重點說說添加隨機Token限制:
token是為了防止表單重復提交,token 原理大致為:
1:顯示表單的那個 action 中使用 createToken() 生成一個隨機的 token值,并存放在服務端(session或者cache中),并且傳遞一份到頁面中
2:表單頁面使用一個隱藏表單域獲取后端傳過來的 token值,該表單頁面提交時會將此 token 值一同提交到后端
3:在表單頁面提交到的 actioin 中使用 validateToken() 將服務端與表單隱藏域中的 token 值進行對比,如果服務端存在 token值并且與表單提交過來的值相等,證明是第一次提交。
4:每次校驗過后服務端的 token 值會立即被清除,所以當用戶重復提交時,后面的提交校驗都再也無法通過。從而實現(xiàn)了防止重復提交的功能,validateToken 是在 synchronized 塊中執(zhí)行的保障了多線程下的安全性。
token 會優(yōu)先存入 me.setTokenCache(ITokenCache) 指定的 TokenCache 中,如果未指定則默認使用 session 來存放
但是這種機制是有問題的,比如我是用ajax提交表單,提交完成以后表單頁面并不刷新,然后我修改了部分數(shù)據(jù)以后再次提交頁面,那么token還是之前的那個token,后臺會以為這個為重復提交不能通過校驗,那么請求就不能完成,數(shù)據(jù)無法得到正確的處理。我認為合理的機制應該是這樣的:
1:顯示表單的那個 action 中使用 createToken() 生成一個隨機的 token值,并且傳遞一份到頁面中
2:表單頁面使用一個隱藏表單域獲取后端傳過來的 token值,該表單頁面提交時會將此 token 值一同提交到后端
3:將提交過來的token值放入session或者cache中,然后執(zhí)行controller中的代碼,代碼全部執(zhí)行完以后,再把存入session或cache中的token值刪除掉;驗證用戶是否為重復提交只需要驗證提交過來的token是否存在于session或cache中,有則為重復提交,無則為正常提交。
4:該邏輯應該可以寫成一個Interceptor,在需要的地方加上,或者直接設為全局攔截器都是可以的,簡單,快捷;
JDK1.6版添加了新的ScriptEngine類,允許用戶直接執(zhí)行js代碼。
在Java中直接調用js代碼
不能調用瀏覽器中定義的js函數(shù),會拋出異常提示ReferenceError: “alert” is not defined。
| package com.sinaapp.manjushri; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; /** * 直接調用js代碼 */ public class ScriptEngineTest { public static void main(String[] args) { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("javascript"); try{ engine.eval("var a=3; var b=4;print (a+b);"); // engine.eval("alert(\"js alert\");"); // 不能調用瀏覽器中定義的js函數(shù) // 錯誤,會拋出alert引用不存在的異常 }catch(ScriptException e){ e.printStackTrace(); } } } |
輸出結果:7
在Java中綁定js變量
在調用engine.get(key);時,如果key沒有定義,則返回null
| package com.sinaapp.manjushri; import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; public class ScriptEngineTest2 { public static void main(String[] args) { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("javascript"); engine.put("a", 4); engine.put("b", 3); Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); try { // 只能為Double,使用Float和Integer會拋出異常 Double result = (Double) engine.eval("a+b"); System.out.println("result = " + result); engine.eval("c=a+b"); Double c = (Double)engine.get("c"); System.out.println("c = " + c); } catch (ScriptException e) { e.printStackTrace(); } } } |
輸出:
result = 7.0
c = 7.0
在Java中調用js文件中的function,傳入調用參數(shù),并獲取返回值
js文件中的merge函數(shù)將兩個參數(shù)a,b相加,并返回c。
| // expression.js function merge(a, b) { c = a * b; return c; } |
在Java代碼中讀取js文件,并參數(shù)兩個參數(shù),然后回去返回值。
| package com.sinaapp.manjushri; import java.io.FileReader; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; /** * Java調用并執(zhí)行js文件,傳遞參數(shù),并活動返回值 * * @author manjushri */ public class ScriptEngineTest { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("javascript"); String jsFileName = "expression.js"; // 讀取js文件 FileReader reader = new FileReader(jsFileName); // 執(zhí)行指定腳本 engine.eval(reader); if(engine instanceof Invocable) { Invocable invoke = (Invocable)engine; // 調用merge方法,并傳入兩個參數(shù) // c = merge(2, 3); Double c = (Double)invoke.invokeFunction("merge", 2, 3); System.out.println("c = " + c); } reader.close(); } } |
輸出結果:
c = 5.0
java調用腳本語言筆記(jython,jruby,groovy)
有兩種方法
1.java se 6以后實現(xiàn)了jsr 223規(guī)范
java代碼:
- ScriptEngineManager factory = new ScriptEngineManager();
- ScriptEngineManager scriptEngine = factory.getEngineByName("javascript");//或者"js"
- scriptEngine.eval(code);//執(zhí)行一段腳本,code是js代碼
很方便調用腳本
2.可以使用腳本語方本身提供的與java的集成手段
jython集成
使用jsr223:
前提下載jython的包,已實現(xiàn)jsr223
(建議在官網上下載,在安裝目錄下有jython.jar,http://repo2.maven.org/maven2/org/python/jython/2.5.0/ 這里也有,但是這個包里沒有jsr223的實現(xiàn),看包下存不存在org.python.jsr223)
- ScriptEngineManager factory = new ScriptEngineManager();
- ScriptEngineManager scriptEngine = factory.getEngineByName("python");//或者"jython"
- scriptEngine.eval(code);
使用PythonInterpreter,可以調用exec(String code)方法:
- PythonInterpreter interpreter = new PythonInterpreter();
- interpreter.exec(code);
訪問數(shù)據(jù)庫
使用jdbc:
- from oracle.jdbc.driver import OracleDriver
- from java.sql import DriverManager
-
- username = 'hr'
- password = '123456'
- url = 'jdbc:oracle:thin:@localhost:1521:XE'
- driver = OracleDriver()
- DriverManager.registerDriver(driver)
- conn = DriverManager.getConnection(url, username, password)
- stmt = conn.createStatement()
- sql = "select salary from EMPLOYEES t where t.salary<2300"
- rs = stmt.executeQuery(sql)
- while (rs.next()):
- print rs.getInt('salary')
- rs.close()
- stmt.close()
結果:
2200
2100
2200
使用zxJDBC :
- from com.ziclix.python.sql import zxJDBC
-
- url = 'jdbc:oracle:thin:@localhost:1521:XE'
- username = 'hr'
- password = '123456'
- driverName = 'oracle.jdbc.driver.OracleDriver'
- mysqlConn = zxJDBC.connect(url,username, password,driverName)
- cursor = mysqlConn.cursor()
- cursor.execute("select last_name from EMPLOYEES t where t.salary<2300");
- #print cursor.fetchone()
- list = cursor.fetchall()
- for record in list:
- print "name:"+record[0]
- #print cursor.description[0]
- #print cursor.description[1]
結果:
name:麥克
name:Olson
name:Philtanker
從數(shù)據(jù)庫中查出的中文內容正常的。
而在代碼里面的中文全部是亂碼或拋異常,未解決。
與jruby集成
使用jsr223:Java代碼
- ScriptEngineManager factory = new ScriptEngineManager();
- ScriptEngineManager scriptEngine = factory.getEngineByName("jruby");//或者"ruby"
- scriptEngine.eval(code);
訪問數(shù)據(jù)庫
Ruby代碼
- require 'java'
-
- module JavaLang
- include_package "java.lang"
- end
-
- module JavaSql
- include_package 'java.sql'
- end
-
- begin
- username = 'hr'
- password = '123456'
- url = 'jdbc:oracle:thin:@localhost:1521:XE'
- driverName = 'oracle.jdbc.driver.OracleDriver'
- JavaLang::Class.forName(driverName).newInstance
- conn = JavaSql::DriverManager.getConnection(url, username, password)
- stmt = conn.createStatement
- sql = "select last_name from EMPLOYEES t where t.salary<2300"
- rs = stmt.executeQuery(sql)
- while (rs.next) do
- puts "名字:"+rs.getString("last_name")
- end
- rs.close
- stmt.close
- conn.close()
- rescue JavaLang::ClassNotFoundException
- puts "ClassNotFoundException"
- rescue JavaSql::SQLException
- puts "SQLException"
- end
結果:
名字:楹﹀厠
名字:Olson
名字:Philtanker
從數(shù)據(jù)庫中查出的中文內容為亂碼的。
而在代碼里面的中文正常。
與groovy集成
使用jsr223:
Java代碼
- ScriptEngineManager factory = new ScriptEngineManager();
- ScriptEngineManager scriptEngine = factory.getEngineByName("groovy");//或者"Groovy"
- scriptEngine.eval(code);
使用GroovyShell:
Java代碼
- GroovyShell shell = new GroovyShell();
- Script script = shell.parse(code);
- Object result = script.run();
訪問數(shù)據(jù)庫
- import groovy.sql.Sql
-
- def username = 'hr'
- def password = '123456'
- def url = 'jdbc:oracle:thin:@localhost:1521:XE'
- def driverName = 'oracle.jdbc.driver.OracleDriver'
- def sql = Sql.newInstance(url, username, password, driverName)
-
- sql.eachRow("select last_name from EMPLOYEES t where t.salary<2300") {
- println "名字:${it.last_name}"
- }
結果:
名字:麥克
名字:Olson
名字:Philtanker
在使用groovy過程中碰到了一個異常
Exception in thread "main" java.lang.VerifyError: (class: groovy/runtime/metaclass/java/util/ArrayListMetaClass, method: super$2$invokeMethod signature: (Ljava/lang/Class;Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;ZZ)Ljava/lang/Object;) Illegal use of nonvirtual function call
這個異常解決花了很長時間
是因為在原來項目中存在json-lib-2.1.jar(有可能名稱為json-lib-2.1-jdk15.jar),這個包是用來處理json的,與groovy1.7.5存在沖突,更新為json-lib-2.3.jar即可
(json-lib里有一些groovy運行時處理的內容)
這兩天Java服務器上忽然遇到這樣的異常:
avax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
問題的根本是:
缺少安全證書時出現(xiàn)的異常。
解決問題方法:
將你要訪問的webservice/url....的安全認證證書導入到客戶端即可。
以下是獲取安全證書的一種方法,通過以下程序獲取安全證書:
/*
* Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Sun Microsystems nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class InstallCert {
public static void main(String[] args)
throws Exception {
String host;
int port;
char[] passphrase;
if ((args.length == 1) || (args.length == 2)) {
String[] c = args[0].split(":");
host = c[0];
port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
String p = (args.length == 1) ? "changeit" : args[1];
passphrase = p.toCharArray();
}
else {
System.out
.println("Usage: java InstallCert <host>[:port] [passphrase]");
return;
}
File file =
new File("jssecacerts");
if (file.isFile() ==
false) {
char SEP = File.separatorChar;
File dir =
new File(System.getProperty("java.home") + SEP + "lib"
+ SEP + "security");
file =
new File(dir, "jssecacerts");
if (file.isFile() ==
false) {
file =
new File(dir, "cacerts");
}
}
System.out.println("Loading KeyStore " + file + "

");
InputStream in =
new FileInputStream(file);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(in, passphrase);
in.close();
SSLContext context = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager) tmf
.getTrustManagers()[0];
SavingTrustManager tm =
new SavingTrustManager(defaultTrustManager);
context.init(
null,
new TrustManager[] { tm },
null);
SSLSocketFactory factory = context.getSocketFactory();
System.out
.println("Opening connection to " + host + ":" + port + "

");
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
socket.setSoTimeout(10000);
try {
System.out.println("Starting SSL handshake

");
socket.startHandshake();
socket.close();
System.out.println();
System.out.println("No errors, certificate is already trusted");
}
catch (SSLException e) {
System.out.println();
e.printStackTrace(System.out);
}
X509Certificate[] chain = tm.chain;
if (chain ==
null) {
System.out.println("Could not obtain server certificate chain");
return;
}
BufferedReader reader =
new BufferedReader(
new InputStreamReader(
System.in));
System.out.println();
System.out.println("Server sent " + chain.length + " certificate(s):");
System.out.println();
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
MessageDigest md5 = MessageDigest.getInstance("MD5");
for (
int i = 0; i < chain.length; i++) {
X509Certificate cert = chain[i];
System.out.println(" " + (i + 1) + " Subject "
+ cert.getSubjectDN());
System.out.println(" Issuer " + cert.getIssuerDN());
sha1.update(cert.getEncoded());
System.out.println(" sha1 " + toHexString(sha1.digest()));
md5.update(cert.getEncoded());
System.out.println(" md5 " + toHexString(md5.digest()));
System.out.println();
}
System.out
.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
String line = reader.readLine().trim();
int k;
try {
k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
}
catch (NumberFormatException e) {
System.out.println("KeyStore not changed");
return;
}
X509Certificate cert = chain[k];
String alias = host + "-" + (k + 1);
ks.setCertificateEntry(alias, cert);
OutputStream out =
new FileOutputStream("jssecacerts");
ks.store(out, passphrase);
out.close();
System.out.println();
System.out.println(cert);
System.out.println();
System.out
.println("Added certificate to keystore 'jssecacerts' using alias '"
+ alias + "'");
}
private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
private static String toHexString(
byte[] bytes) {
StringBuilder sb =
new StringBuilder(bytes.length * 3);
for (
int b : bytes) {
b &= 0xff;
sb.append(HEXDIGITS[b >> 4]);
sb.append(HEXDIGITS[b & 15]);
sb.append(' ');
}
return sb.toString();
}
private static class SavingTrustManager
implements X509TrustManager {
private final X509TrustManager tm;
private X509Certificate[] chain;
SavingTrustManager(X509TrustManager tm) {
this.tm = tm;
}
public X509Certificate[] getAcceptedIssuers() {
throw new UnsupportedOperationException();
}
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
throw new UnsupportedOperationException();
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
this.chain = chain;
tm.checkServerTrusted(chain, authType);
}
}
}
編譯InstallCert.java,然后執(zhí)行:java InstallCert hostname,比如:
java InstallCert www.twitter.com
會看到如下信息:
java InstallCert www.twitter.com
Loading KeyStore /usr/java/jdk1.6.0_16/jre/lib/security/cacerts

Opening connection to www.twitter.com:443

Starting SSL handshake

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1476)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:846)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:815)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1025)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1038)
at InstallCert.main(InstallCert.java:63)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:221)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:145)
at sun.security.validator.Validator.validate(Validator.java:203)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
at InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.java:158)
at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:839)

7 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:236)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:194)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:216)

13 more
Server sent 2 certificate(s):
1 Subject CN=www.twitter.com, O=example.com, C=US
Issuer CN=Certificate Shack, O=example.com, C=US
sha1 2e 7f 76 9b 52 91 09 2e 5d 8f 6b 61 39 2d 5e 06 e4 d8 e9 c7
md5 dd d1 a8 03 d7 6c 4b 11 a7 3d 74 28 89 d0 67 54
2 Subject CN=Certificate Shack, O=example.com, C=US
Issuer CN=Certificate Shack, O=example.com, C=US
sha1 fb 58 a7 03 c4 4e 3b 0e e3 2c 40 2f 87 64 13 4d df e1 a1 a6
md5 72 a0 95 43 7e 41 88 18 ae 2f 6d 98 01 2c 89 68
Enter certificate to add to trusted keystore or 'q' to quit: [1]
輸入1,回車,然后會在當前的目錄下產生一個名為“ssecacerts”的證書。
將證書拷貝到$JAVA_HOME/jre/lib/security目錄下,或者通過以下方式:
System.setProperty("javax.net.ssl.trustStore", "你的jssecacerts證書路徑");
注意:因為是靜態(tài)加載,所以要重新啟動你的Web Server,證書才能生效。
試了以上的方法,后來發(fā)現(xiàn)還不行。最后突然心血來潮:我把Myeclipse關閉,直接啟動Tomcat,然后運行,居然就可以了。具體原因沒有找到。估計是
我的Myeclipse引用的JDK引用不對。后來就沒有具體找原因了。