??xml version="1.0" encoding="utf-8" standalone="yes"?> // 初始化可以拖拽的Element的函敎ͼ与拖拽无关的我去掉了 // 可拖拽Element的原形,用来eventl定到各个钩子,q部分市比较通用的,netvibes也是基本完全相同的实? // 下面是初始化的函CQ看看上面这些东西怎么被调? // google的页面里可以拖动的部分的id?t_1" // 请将下面两条被注释掉的代码加Q到你自׃载的一个google ig面里面Q把里面的所有其余删除,挂上q个js也可以拖拽了Q哈? 使搜索引搜烦蜘蛛的收录由被动变ؓdQ?span style="font-family: Times New Roman;">google Sitemap服务目前不提供在线创徏Sitemap的功能,但是我们可以借助W三方网站提供的此类服务Q打开|站Q?a >http://www.xml-sitemaps.comQ(如图1Q?nbsp; ?#8220;Starting URL”栏中输入你自q|站地址Q在“Change frequency”下拉列表选择|站的更新频率(l常 每天 每年{不同的时段Q,?#8220;Last modification”选择最后修Ҏ_选择Use server's response服务器反映时?nbsp;Q,?#8220;Priority”栏中速入跟新的优先权Q最后点?#8220;Start”按钮创徏|站地图 Q如?Q?img src="http://www.6b9g.com/sitemap0.gif" onclick="javascript:window.open(this.src);" style="cursor: pointer;" onload="rsimg(this,500)" alt="" /> 完成操作后,我们在昄的结果中看到各式各样的网站生成地图Sitemap文gQ将“Sitemap.xml”下蝲到本地然后再传到服务器主目录下,下面用Gmail帐户登陆到google Sitemap,d自己的网站sitemap文gQ如?Q?img src="http://www.6b9g.com/sitemap.gif" onclick="javascript:window.open(this.src);" style="cursor: pointer;" onload="rsimg(this,500)" alt="" />Q再选在d该站点的sitemapQ注意在选择分类的时候选择d常规|络(如图3) 首发 高峰 站长互动信息|?nbsp; http://www.6b9g.com
var Util = new Object();
// 获取http header里面的UserAgentQ浏览器信息
Util.getUserAgent = navigator.userAgent;
// 是否是Gecko核心的BrowserQ比如Mozila、Firefox
Util.isGecko = Util.getUserAgent.indexOf( " Gecko " ) != - 1 ;
// 是否是Opera
Util.isOpera = Util.getUserAgent.indexOf( " Opera " ) != - 1 ;
// 获取一个element的offset信息Q其实就是相对于Body的padding以内的绝对坐?/span>
// 后面一个参数如果是true则获取offsetLeftQfalse则是offsetTop
// 关于offset、style、client{坐标的定义参考dindin的这个帖子:http://www.jroller.com/page/dindin/?anchor=pro_java_12
Util.getOffset = (el, isLeft) {
var ret = 0 ;
while (el != null ) {
ret += el[ " offset " + (isLeft ? " Left " : " Top " )];
el = el.offsetParent;
}
return ret;
};
// 一个(参数中的funcName是这个fuction的名字)l定C个element上,q且以这个element的上下文q行Q其实是一U承,q个可以google些文章看?/span>
Util.bind = (el, fucName) {
return () {
return el[fucName].apply(el, arguments);
};
};
// 重新计算所有的可以拖拽的element的坐标,对同一个column下面的可拖拽囑ֱ重新计算它们的高度而得出新的坐标,防止遮叠
// 计算出来的坐标记录在pagePosLeft和pagePosTop两个属性里?/span>
Util.re_calcOff = (el) {
for ( var i = 0 ; i < Util.dragArray.length; i ++ ) {
var ele = Util.dragArray[i];
ele.elm.pagePosLeft = Util.getOffset(ele.elm, true );
ele.elm.pagePosTop = Util.getOffset(ele.elm, false );
}
var nextSib = el.elm.nextSibling;
while (nextSib) {
nextSib.pagePosTop -= el.elm.offsetHeight;
nextSib = nextSib.nextSibling;
}
};
// 隐藏Google Ig中间那个tableQ也是拖拽的容器,配合show一般就是刷新用Q解决一些浏览器的怪癖
Util.hide = () {
Util.rootElement.style.display = " none " ;
};
// 昄Google Ig中间那个tableQ解释同?/span>
Util.show = () {
Util.rootElement.style.display = "" ;
};
// Ud时显C的占位虚线?/span>
ghostElement = null ;
// 获取q个虚线框,通过dom动态生?/span>
getGhostElement = () {
if ( ! ghostElement) {
ghostElement = createElement( " DIV " );
ghostElement.className = " modbox " ;
ghostElement.backgroundColor = "" ;
ghostElement.style.border = " 2px dashed #aaa " ;
ghostElement.innerHTML = " " ;
}
return ghostElement;
};
draggable(el) {
// 公用的开始拖拽的函数
this ._dragStart = start_Drag;
// 公用的正在拖拽的函数
this ._drag = when_Drag;
// 公用的拖拽结束的函数
this ._dragEnd = end_Drag;
// q个函数主要用来q行拖拽l束后的dom处理
this ._afterDrag = after_Drag;
// 是否正在被拖动,一开始当然没有被拖动
this .isDragging = false ;
// 这个Element的this指针注册在elmq个变量里面Q方便在自己的上下文以外调用自己的函数等Q很常用的方?
this .elm = el;
// 触发拖拽的ElementQ在q里是q个div上显C标题的那个div
this .header = getElementById(el.id + " _h " );
// 对于有i的element拖拽不同Q这里检一下ƈ记录
this .hasI = this .elm.getElementsByTagName( " I " ).length > 0 ;
// 如果扑ֈ了headerq定drag相关的event
if ( this .header) {
// 拖拽时的叉子鼠标指针
this .header.style.cursor = " move " ;
// 函数绑定到header和element的this上,参照那个函数的说?
Drag.init( this .header, this .elm);
// 下面三个语句写好的三个函数l定l这个elemnt的三个函数钩子上Q也实Celement从draggablel承可拖拽的函数
this .elm.onDragStart = Util.bind( this , " _dragStart " );
this .elm.onDrag = Util.bind( this , " _drag " );
this .elm.onDragEnd = Util.bind( this , " _dragEnd " );
}
};
// 公用的开始拖拽的函数
start_Drag() {
// 重置坐标Q实现拖拽以后自q位置马上会被填充的效?
Util.re_calcOff( this );
// 记录原先的邻居节点,用来Ҏ是否被移动到新的位置
this .origNextSibling = this .elm.nextSibling;
// 获取Ud的时候那个灰色的虚线?
var _ghostElement = getGhostElement();
// 获取正在Ud的这个对象的高度
var offH = this .elm.offsetHeight;
if (Util.isGecko) {
// 修正gecko引擎的怪癖?
offH -= parseInt(_ghostElement.style.borderTopWidth) * 2 ;
}
// 获取正在Ud的这个对象的宽度
var offW = this .elm.offsetWidth;
// 获取left和top的坐?
var offLeft = Util.getOffset( this .elm, true );
var offTop = Util.getOffset( this .elm, false );
// 防止闪烁Q现隐藏
Util.hide();
// 自q宽度记录在style属性里?
this .elm.style.width = offW + " px " ;
// 那个灰框设定得与正在拖动的对象一样高Q比较Ş?
_ghostElement.style.height = offH + " px " ;
// 把灰框放到这个对象原先的位置?
this .elm.parentNode.insertBefore(_ghostElement, this .elm.nextSibling);
// ׃要拖动必d被拖动的对象从原先的盒子模型里面抽出来,所以设定position为absoluteQ这个可以参考一下css布局斚w的知?
this .elm.style.position = " absolute " ;
// 讄zIndexQ让它处在最前面一层,当然其实zIndex=100是让它很靠前Q如果页面里有zIndex>100的,?#8230;…
this .elm.style.zIndex = 100 ;
// ׃position=absolute了,所以left和top实现l对坐标定位Q这是先前计算坐标的作用,不让q个模型pQ要从开始拖动的地方开始移?
this .elm.style.left = offLeft + " px " ;
this .elm.style.top = offTop + " px " ;
// 坐标讑֮完毕Q可以显CZQ这样就不会闪烁?
Util.show();
// q里本来有个ig_d.GQ没搞明白干什么用的,不过没有也可以用Q谁知道ȝ告诉我一壎ͼ不好意?
// q没有开始拖拽,q里做个记号
this .isDragging = false ;
return false ;
};
draggable(el) {
// 公用的开始拖拽的函数
this ._dragStart = start_Drag;
// 公用的正在拖拽的函数
this ._drag = when_Drag;
// 公用的拖拽结束的函数
this ._dragEnd = end_Drag;
// q个函数主要用来q行拖拽l束后的dom处理
this ._afterDrag = after_Drag;
// 是否正在被拖动,一开始当然没有被拖动
this .isDragging = false ;
// 这个Element的this指针注册在elmq个变量里面Q方便在自己的上下文以外调用自己的函数等Q很常用的方?
this .elm = el;
// 触发拖拽的ElementQ在q里是q个div上显C标题的那个div
this .header = getElementById(el.id + " _h " );
// 对于有i的element拖拽不同Q这里检一下ƈ记录
this .hasI = this .elm.getElementsByTagName( " I " ).length > 0 ;
// 如果扑ֈ了headerq定drag相关的event
if ( this .header) {
// 拖拽时的叉子鼠标指针
this .header.style.cursor = " move " ;
// 函数绑定到header和element的this上,参照那个函数的说?
Drag.init( this .header, this .elm);
// 下面三个语句写好的三个函数l定l这个elemnt的三个函数钩子上Q也实Celement从draggablel承可拖拽的函数
this .elm.onDragStart = Util.bind( this , " _dragStart " );
this .elm.onDrag = Util.bind( this , " _drag " );
this .elm.onDragEnd = Util.bind( this , " _dragEnd " );
}
};
// 公用的开始拖拽的函数
start_Drag() {
// 重置坐标Q实现拖拽以后自q位置马上会被填充的效?
Util.re_calcOff( this );
// 记录原先的邻居节点,用来Ҏ是否被移动到新的位置
this .origNextSibling = this .elm.nextSibling;
// 获取Ud的时候那个灰色的虚线?
var _ghostElement = getGhostElement();
// 获取正在Ud的这个对象的高度
var offH = this .elm.offsetHeight;
if (Util.isGecko) {
// 修正gecko引擎的怪癖?
offH -= parseInt(_ghostElement.style.borderTopWidth) * 2 ;
}
// 获取正在Ud的这个对象的宽度
var offW = this .elm.offsetWidth;
// 获取left和top的坐?
var offLeft = Util.getOffset( this .elm, true );
var offTop = Util.getOffset( this .elm, false );
// 防止闪烁Q现隐藏
Util.hide();
// 自q宽度记录在style属性里?
this .elm.style.width = offW + " px " ;
// 那个灰框设定得与正在拖动的对象一样高Q比较Ş?
_ghostElement.style.height = offH + " px " ;
// 把灰框放到这个对象原先的位置?
this .elm.parentNode.insertBefore(_ghostElement, this .elm.nextSibling);
// ׃要拖动必d被拖动的对象从原先的盒子模型里面抽出来,所以设定position为absoluteQ这个可以参考一下css布局斚w的知?
this .elm.style.position = " absolute " ;
// 讄zIndexQ让它处在最前面一层,当然其实zIndex=100是让它很靠前Q如果页面里有zIndex>100的,?#8230;…
this .elm.style.zIndex = 100 ;
// ׃position=absolute了,所以left和top实现l对坐标定位Q这是先前计算坐标的作用,不让q个模型pQ要从开始拖动的地方开始移?
this .elm.style.left = offLeft + " px " ;
this .elm.style.top = offTop + " px " ;
// 坐标讑֮完毕Q可以显CZQ这样就不会闪烁?
Util.show();
// q里本来有个ig_d.GQ没搞明白干什么用的,不过没有也可以用Q谁知道ȝ告诉我一壎ͼ不好意?
// q没有开始拖拽,q里做个记号
this .isDragging = false ;
return false ;
};
// 在拖拽时的相应函敎ͼ׃l定到鼠标的moveq个event上,所以会传入鼠标的坐标clientX, clientY
when_Drag(clientX, clientY) {
// 刚开始拖拽的时候将囑ֱ变透明Qƈ标记为正在被拖拽
if ( ! this .isDragging) {
this .elm.style.filter = " alpha(opacity=70) " ;
this .elm.style.opacity = 0.7 ;
this .isDragging = true ;
}
// 被拖拽到的新的columnQ当然也可以是原来那个)
var found = null ;
// 最大的距离Q可能是防止溢出或者什么bug
var max_distance = 100000000 ;
// 遍历所有的可拖拽的elementQ寻扄当前鼠标坐标最q的那个可拖拽元素,以便后面插入
for ( var i = 0 ; i < Util.dragArray.length; i ++ ) {
var ele = Util.dragArray[i];
// 利用勾股定理计算鼠标到遍历到的这个元素的距离
var distance = Math.sqrt(Math.pow(clientX - ele.elm.pagePosLeft,
2 ) + Math.pow(clientY - ele.elm.pagePosTop, 2 ));
// 自己已经动了,所以不计算自己?
if (ele == this ) {
continue ;
}
// 如果计算p|l箋循环
if (isNaN(distance)) {
continue ;
}
// 如果更小Q记录下q个距离Qƈ它作ؓfound
if (distance < max_distance) {
max_distance = distance;
found = ele;
}
}
// 准备让灰框落?
var _ghostElement = getGhostElement();
// 如果扑ֈ了另外的落脚?
if (found != null && _ghostElement.nextSibling != found.elm) {
// 扑ֈ落脚点就先把灰框插进去,q就是我们看到的那个灰框停靠的特效,有点像吸附的感觉Q哈?
found.elm.parentNode.insertBefore(_ghostElement, found.elm);
if (Util.isOpera) {
// Opera的现实问题,要隐?昄后才能刷新出变化
body.style.display = " none " ;
body.style.display = "" ;
}
}
};
// 拖拽完毕
end_Drag() {
// 拖拽完毕后执行后面的钩子Q执行after_Drag()Q如果布局发生了变动了p录到q程服务器,保存你拖拽后新的布局序
if ( this ._afterDrag()) {
// remote call to save the change
}
return true ;
};
// 拖拽后的执行钩子
after_Drag() {
var return = false ;
// 防止闪烁
Util.hide();
// 把拖拽时的position=absolute和相关的那些style都消?
this .elm.style.position = "" ;
this .elm.style.width = "" ;
this .elm.style.zIndex = "" ;
this .elm.style.filter = "" ;
this .elm.style.opacity = "" ;
// 获取灰框
var ele = getGhostElement();
// 如果现在的邻居不是原来的d?
if (ele.nextSibling != this .origNextSibling) {
// 把被拖拽的这个节Ҏ到灰框的前面
ele.parentNode.insertBefore( this .elm, ele.nextSibling);
// 标明被拖拽了新的地方
return = true ;
}
// U除灰框Q这是这个灰框的生命周期应该q束了
ele.parentNode.removeChild(ele);
// 修改完毕Q显C?
Util.show();
if (Util.isOpera) {
// Opera的现实问题,要隐?昄后才能刷新出变化
body.style.display = " none " ;
body.style.display = "" ;
}
return return;
};
// q部分推荐看dindin的这个,也会帮助理解Q?a >http://www.jroller.com/page/dindin/?anchor=pro_java_12
var Drag = {
// 对这个element的引用,一ơ只能拖拽一个Element
obj: null ,
// element是被拖拽的对象的引用QelementHeader是鼠标可以拖拽的区?
init: (elementHeader, element) {
// startl定到down事gQ按下鼠标触发start
elementHeader.down = Drag.start;
// element存到header的obj里面Q方便header拖拽的时候引?
elementHeader.obj = element;
// 初始化绝对的坐标Q因Z是position=absolute所以不会v什么作用,但是防止后面onDrag的时候parse出错?
if (isNaN(parseInt(element.style.left))) {
element.style.left = " 0px " ;
}
if (isNaN(parseInt(element.style.top))) {
element.style.top = " 0px " ;
}
// 挂上I,初始化这几个成员Q在Drag.init被调用后才帮定到实际的函敎ͼ可以参照draggable里面的内?
element.onDragStart = new ();
element.onDragEnd = new ();
element.onDrag = new ();
},
// 开始拖拽的l定Q绑定到鼠标的移动的event?
start: (event) {
var element = Drag.obj = this .obj;
// 解决不同览器的event模型不同的问?
event = Drag.fixE(event);
// 看看是不是左键点?
if (event.which != 1 ) {
// 除了左键都不起作?
return true ;
}
// 参照q个函数的解释,挂上开始拖拽的钩子
element.onDragStart();
// 记录鼠标坐标
element.lastMouseX = event.clientX;
element.lastMouseY = event.clientY;
// Global的eventl定到被拖动的element上面?
up = Drag.end;
move = Drag.drag;
return false ;
},
// Element正在被拖动的函数
drag: (event) {
// 解决不同览器的event模型不同的问?
event = Drag.fixE(event);
// 看看是不是左键点?
if (event.which == 0 ) {
// 除了左键都不起作?
return Drag.end();
}
// 正在被拖动的Element
var element = Drag.obj;
// 鼠标坐标
var _clientX = event.clientY;
var _clientY = event.clientX;
// 如果鼠标没动׃么都不作
if (element.lastMouseX == _clientY && element.lastMouseY == _clientX) {
return false ;
}
// 刚才Element的坐?
var _lastX = parseInt(element.style.top);
var _lastY = parseInt(element.style.left);
// 新的坐标
var newX, newY;
// 计算新的坐标Q原先的坐标+鼠标Ud的值差
newX = _lastY + _clientY - element.lastMouseX;
newY = _lastX + _clientX - element.lastMouseY;
// 修改element的显C坐?
element.style.left = newX + " px " ;
element.style.top = newY + " px " ;
// 记录element现在的坐标供下一ơ移动?
element.lastMouseX = _clientY;
element.lastMouseY = _clientX;
// 参照q个函数的解释,挂接上Drag时的钩子
element.onDrag(newX, newY);
return false ;
},
// Element正在被释攄函数Q停止拖?
end: (event) {
// 解决不同览器的event模型不同的问?
event = Drag.fixE(event);
// 解除对Global的event的绑?
move = null ;
up = null ;
// 先记录下onDragEnd的钩子,好移除obj
var _onDragEndFuc = Drag.obj.onDragEnd();
// 拖拽完毕Qobj清空
Drag.obj = null ;
return _onDragEndFuc;
},
// 解决不同览器的event模型不同的问?
fixE: (ig_) {
if ( typeof ig_ == " undefined " ) {
ig_ = event;
}
if ( typeof ig_.layerX == " undefined " ) {
ig_.layerX = ig_.offsetX;
}
if ( typeof ig_.layerY == " undefined " ) {
ig_.layerY = ig_.offsetY;
}
if ( typeof ig_.which == " undefined " ) {
ig_.which = ig_.button;
}
return ig_;
}
};
var _IG_initDrag = (el) {
// column那个容器Q在google里面是那个table布局的tbodyQnetvibes用的<div>
Util.rootElement = el;
// q个tbody的行
Util._rows = Util.rootElement.tBodies[ 0 ].rows[ 0 ];
// 列,google?列,其实也可以更?
Util.column = Util._rows.cells;
// 用来存取可拖拽的对象
Util.dragArray = new Array();
var counter = 0 ;
for ( var i = 0 ; i < Util.column.length; i ++ ) {
// 搜烦所有的column
var ele = Util.column[i];
for ( var j = 0 ; j < ele.childNodes.length; j ++ ) {
// 搜烦每一column里面的所有element
var ele1 = ele.childNodes[j];
// 如果是div把它初始化Z个draggable对象
if (ele1.tagName == " DIV " ) {
Util.dragArray[counter] = new draggable(ele1);
counter ++ ;
}
}
}
};
// 挂蝲刎ͼ载入完毕执行。不q实际上google没有用?
// 而是写在面最下面Q异曲同工吧Q也许直接写在页面是U怪癖Q或者也有可能是兼容性考虑?
// _table=getElementById("t_1");
// = _IG_initDrag(_table);
]]>sitemap然后输入刚才上传得那个sitemap.xml地址Q各?a >http://www.6b9g.com/sitemap.xml 再点?#8220;d普通sitemap” 按钮
]]>
本程序用了HTTPClient包,下蝲地址Q?br />
http://www.innovation.ch/java/HTTPClient/
灌水机原理很单,是分析论坛的表单,用自qE序模拟提交可以了Q?br />
本文的目的在于介lHTTPClientq个开源工P比jdk?net包强何止癑ր,
HTTPClient的特Ҏ多个操作可以复用同一个连接,讄q接时(Zsocket)Q用代理验证。具体可以到innovation|站看看Ҏ数据?br />
如下是灌水程序的单程序,仅供参?br />
import java.net.*;
import java.io.*;
import java.util.*;
import HTTPClient.*;
class WebRequester{
private static InputStream istr = null;
private static OutputStream ostr = null;
private static NVPair form_data[];
private static HTTPConnection httpCon;
private static HTTPResponse rsp;
private static String host;
private static WebRequester instance;
private WebRequester(){
}
public static WebRequester getInstance(){
if(instance==null){
instance = new WebRequester();
}
return instance;
}
public static String request(HTTPConnection connection,String pathName,String method,NVPair form_data[]) {
try{
httpCon = connection;
if(method.toLowerCase().equals("get")){
if(form_data!=null)
rsp = httpCon.Get(pathName, form_data);
else
rsp = httpCon.Get(pathName);
}
else{
if(form_data!=null)
rsp = httpCon.Post(pathName, form_data);
else
rsp = httpCon.Post(pathName);
}
istr = rsp.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(istr));
String line;
StringBuffer result = new StringBuffer();
while ((line = reader.readLine()) != null) {
result.append(line + System.getProperty("line.separator"));
}
return result.toString();
} catch(Exception e){
}
return "";
}
}
public class Flood
{
private HTTPConnection connection;
public Flood(){
getConnection("sitename.com",80);
}
public void releaseConnection(){
if(connection!=null){
connection.stop();
connection = null;
}
}
public HTTPClient.HTTPConnection getConnection(String hostName,int port){
if(connection==null){
try{
connection = new HTTPClient.HTTPConnection(hostName,port);
HTTPClient.Module.setPolicyHandler(null);
connection.addDefaultModule(Class.forName("HTTPClient.Module"), 1);
connection.addModule(Class.forName("HTTPClient.RedirectionModule"),2);
}catch(Exception e){
e.printStackTrace();
}
}
return connection;
}
public void post(String subject,String body){
NVPair[] form_data = new NVPair[5];
form_data[0] = new NVPair("forumID","87");
form_data[1] = new NVPair("subject",subject);
form_data[2] = new NVPair("classifier","-1");
form_data[3] = new NVPair("body",body);
form_data[4] = new NVPair("doPost"," ??");
WebRequester.getInstance().request(connection,"post!post.jspa","post",form_data);
}
public void reply(String thread,String subject,String body){
//提交表单需要多项Q查看回复页面表单可以获?br />
NVPair[] form_data = new NVPair[7];
form_data[0] = new NVPair("forumID","87");
form_data[1] = new NVPair("subject",subject);
form_data[2] = new NVPair("classifier","-1");
form_data[3] = new NVPair("body",body);
form_data[4] = new NVPair("reply","true");
form_data[5] = new NVPair("threadID",thread);
form_data[6] = new NVPair("doPost"," ??");
WebRequester.getInstance().request(connection,"post!post.jspa","post",form_data);
}
public void finish(){
releaseConnection();
}
public void login(){
WebRequester wr = WebRequester.getInstance();
NVPair[] form_data = new NVPair[4];
//对应登陆需要的表单字段填写
form_data[0] = new NVPair("formUsername","user");
form_data[1] = new NVPair("formPassword","pass");
form_data[2] = new NVPair("formLogins cript","sitename.com/loginuser.jsp");
form_data[3] = new NVPair("forumLogin","Y");
//提交到指定登陆页?br />
wr.request(connection,"cgi-bin/gzhome/registration/LoginUser1.jsp","post",form_data);
//假如重定向,必须用该链接再次h新的面
wr.request(connection,"loginuser.jsp","get",null);
wr.request(connection,"index.jspa","get",null);
}
public static void main(String[] args)
{
try{
Flood f=new Flood();
f.login();
//post一个新主题Qidql自׃?br />
//f.post("friends","剧本");
//得到某个主题idQ进行指定数量的跟帖
for(int i=0;i<50;i++){
f.reply("67145","Re: 警告Q在U朋友发a又少了,望奔走相?,"z水来了");
}
/*如下是读取某个文件每一行文字作为回帖进行灌?br />
BufferedReader
br = new BufferedReader(new FileReader("E:""movie""101-105""Friends -
1x04 - TOW George Stephanopoulos.CHN.srt"));
StringBuffer sb = new StringBuffer();
String t = null;
int counter=0;
while((t=br.readLine())!=null){
if(t.length()==0){
//System.out.println(sb.toString());
//System.out.println("====");
counter++;
f.reply("66617","" + counter,sb.toString());
sb.delete(0,sb.length());
}
sb.append(t + ""n");
}*/
f.finish();
}catch(Exception e){
e.printStackTrace();
}
}
}
OKOK~大家自己研究
我做好了
]]>
public static byte[] getAsciiBytes(final String data) {
if (data == null) {
throw new IllegalArgumentException("Parameter may not be null"); }
try { return data.getBytes("US-ASCII"); }
catch (UnsupportedEncodingException e) {throw new HttpClientError("HttpClient requires ASCII support"); }
}
?
C没有Qreturn
data.getBytes("US-ASCII");它的~码方式是US-ASCIIQ问题就出在q里了,把这个取掉,换成"GBK"或?
"GB2312"保存以后~译Q重新运行程序,goooooooooooood。中文名文g现在可以上传了,呵呵
Introducing FileUpload
The FileUpload component has the
capability of simplifying the handling of files uploaded to a server.
Note that the FileUpload component is meant for use on the server side;
in other words, it handles where the files are being uploaded to—not
the client side where the files are uploaded from. Uploading files from
an HTML form is pretty simple; however, handling these files when they
get to the server is not that simple. If you want to apply any rules
and store these files based on those rules, things get more difficult.
The FileUpload component remedies this situation, and in very few lines of code you can easily manage the files uploaded and store them in appropriate locations. You will now see an example where you upload some files first using a standard HTML form and then using HttpClient code.
Using HTML File Upload
The commonly used methodology to upload
files is to have an HTML form where you define the files you want to
upload. A common example of this HTML interface is the Web page you
encounter when you want to attach files to an email while using any of
the popular Web mail services.
In this example, you will create a simple HTML page where you provide for three files to be uploaded. Listing 1-1 shows the HTML for this page. Note that the enctype attribute for the form has the multipart/form-data, and the input tag used is of type file. Based on the of the action attribute, on form submission, the data is sent to ProcessFileUpload.jsp.
Listing 1-1. UploadFiles.html
<HTML>
<HEAD>
< HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"/>
<TITLE>File Upload Page</TITLE>
</HEAD>
<BODY>Upload Files
<FORM name="filesForm" action="ProcessFileUpload.jsp"
method="post" enctype="multipart/form-data">
File 1:<input type="file" name="file1"/><br/>
File 2:<input type="file" name="file2"/><br/>
File 3:<input type="file" name="file3"/><br/>
<input type="submit" name="Submit" ="Upload Files"/>
</FORM>
</BODY>
</HTML>
You can use a servlet to handle the file upload. I have used JSP to minimize the code you need to write. The task that the JSP has to accomplish is to pick up the files that are sent as part of the request and store these files on the server. In the JSP, instead of displaying the result of the upload in the Web browser, I have chosen to print messages on the server console so that you can use this same JSP when it is not invoked through an HTML form but by using HttpClient-based code.
Listing 1-2 shows the JSP code. Note the code that checks whether the item is a form field. This check is required because the Submit button contents are also sent as part of the request, and you want to distinguish between this data and the files that are part of the request. You have set the maximum file size to 1,000,000 bytes using the setSizeMax method.
Listing 1-2. ProcessFileUpload.jsp
<%@ page contentType="text/html;charset=windows-1252"%>
<%@ page import="org.apache.commons.fileupload.DiskFileUpload"%>
<%@ page import="org.apache.commons.fileupload.FileItem"%>
<%@ page import="jsp servlet ejb .util.List"%>
<%@ page import="jsp servlet ejb .util.Iterator"%>
<%@ page import="jsp servlet ejb .io.File"%>
html>
<head>
< http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>Process File Upload</title>
</head>
<%
System.out.println("Content Type ="+request.getContentType());
DiskFileUpload fu = new DiskFileUpload();
// If file size exceeds, a FileUploadException will be thrown
fu.setSizeMax(1000000);
List fileItems = fu.parseRequest(request);
Iterator itr = fileItems.iterator();
while(itr.hasNext()) {
FileItem fi = (FileItem)itr.next();
//Check if not form field so as to only handle the file inputs
//else condition handles the submit button input
if(!fi.isFormField()) {
System.out.println(""nNAME: "+fi.getName());
System.out.println("SIZE: "+fi.getSize());
//System.out.println(fi.getOutputStream().toString());
File fNew= new File(application.getRealPath("/"), fi.getName());
System.out.println(fNew.getAbsolutePath());
fi.write(fNew);
}
else {
System.out.println("Field ="+fi.getFieldName());
}
}
%>
<body>
Upload Successful!!
</body>
</html>
CAUTION With FileUpload 1.0 I found that when the form was submitted using Opera version 7.11, the getName method of the class FileItem returns just the name of the file. However, if the form is submitted using Internet Explorer 5.5, the filename along with its entire path is returned by the same method. This can cause some problems.
To run this example, you can use any three files, as the contents of the files are not important. Upon submitting the form using Opera and uploading three random XML files, the output I got on the Tomcat server console was as follows:
Content Type =multipart/form-data; boundary=----------rz7ZNYDVpN1To8L73sZ6OE
NAME: academy.xml
SIZE: 951
D:"javaGizmos"jakarta-tomcat-4.0.1"webapps"HttpServerSideApp"academy.xml
NAME: academyRules.xml
SIZE: 1211
D:"javaGizmos"jakarta-tomcat-4.0.1"webapps"HttpServerSideApp"academyRules.xml
NAME: students.xml
SIZE: 279
D:"javaGizmos"jakarta-tomcat-4.0.1"webapps"HttpServerSideApp"students.xml
Field =Submit
However, when submitting this same form using Internet Explorer 5.5, the output on the server console was as follows:
Content Type =multipart/form-data; boundary=---------------------------7d3bb1de0
2e4
NAME: D:"temp"academy.xml
SIZE: 951
D:"javaGizmos"jakarta-tomcat-4.0.1"webapps"HttpServerSideApp"D:"temp"academy.xml
The browser displayed the following message: “The requested resource (D:"javaGizmos"jakarta-tomcat-4.0.1"webapps"HttpServerSideApp"D:"temp"academy.xml (The filename, directory name, or volume label syntax is incorrect)) is not available.”
This contrasting behavior on different browsers can cause problems. One workaround that I found in an article at http://www.onjava.com/pub/a/onjava/2003/06/25/commons.html is to first create a file reference with whatever is supplied by the getName method and then create a new file reference using the name returned by the earlier file reference. Therefore, you can insert the following code to have your code work with both browsers (I wonder who the guilty party is…blaming Microsoft is always the easy way out)
File tempFileRef = new File(fi.getName());
File fNew = new File(application.getRealPath("/"),tempFileRef.getName());
In this section, you uploaded files using a standard HTML form mechanism. However, often a need arises to be able to upload files from within your jsp servlet ejb code, without any browser or form coming into the picture. In the next section, you will look at HttpClient-based file upload.
Using HttpClient-Based FileUpload
Earlier in the article you saw
some of the capabilities of the HttpClient component. One capability I
did not cover was its ability to send multipart requests. In this
section, you will use this capability to upload a few files to the same
JSP that you used for uploads using HTML.
The class org.apache.commons.httpclient.methods.MultipartPostMethod provides the multipart method capability to send multipart-encoded forms, and the package org.apache.commons.httpclient.methods.multipart has the support classes required. Sending a multipart form using HttpClient is quite simple. In the code in Listing 1-3, you send three files to ProcessFileUpload.jsp.
Listing 1-3. HttpMultiPartFileUpload.java
package com.commonsbook.chap9;
import jsp servlet ejb .io.File;
import jsp servlet ejb .io.IOException;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.MultipartPostMethod;
public class HttpMultiPartFileUpload {
private static String url =
"http://localhost/yaoliang/ProcessFileUpload.jsp";
public static void main(String[] args) throws IOException {
HttpClient client = new HttpClient();
MultipartPostMethod mPost = new MultipartPostMethod(url);
client.setConnectionTimeout(8000);
// Send any XML file as the body of the POST request
File f1 = new File("D:/students.xml");
File f2 = new File("D:/demy.xml");
File f3 = new File("D:/demyRules.xml");
System.out.println("File1 Length = " + f1.length());
System.out.println("File2 Length = " + f2.length());
System.out.println("File3 Length = " + f3.length());
mPost.addParameter(f1.getName(),f1.getName(), f1);
mPost.addParameter(f2.getName(), f2.getName(), f2);
mPost.addParameter(f3.getName(), f3.getName(),f3);
FilePart part1 = new FilePart("file1",file);
FilePart part2 = new FilePart("file2",file);
FilePart part3 = new FilePart("file3",file);
mPost.addPart(part1);
mPost.addPart(part2);
mPost.addPart(part3);
int statusCode1 = client.executeMethod(mPost);
System.out.println("statusLine>>>" + mPost.getStatusLine());
mPost.releaseConnection();
}
}
In this code, you just add the files as parameters and execute the method. The ProcessFileUpload.jsp file gets invoked, and the output is as follows:
Content Type =multipart/form-data; boundary=----------------31415926535897932384
6
NAME: students.xml
SIZE: 279
D:"javaGizmos"jakarta-tomcat-4.0.1"webapps"HttpServerSideApp"students.xml
NAME: academy.xml
SIZE: 951
D:"javaGizmos"jakarta-tomcat-4.0.1"webapps"HttpServerSideApp"academy.xml
NAME: academyRules.xml
SIZE: 1211
D:"javaGizmos"jakarta-tomcat-4.0.1"webapps"HttpServerSideApp"academyRules.xml
Thus, file uploads on the server side become quite a simple task if you are using the Commons FileUpload component.
我们不可能列举所 有可能的固Q我们会针对几种最常见的问题进行处理。当然了Q正如前面说到的Q如果我们自׃用java.net.HttpURLConnection? 搞定q些问题是很恐怖的事情Q因此在开始之前我们先要介l一下一个开放源码的目Q这个项目就是Apache开源组l中的httpclientQ它隶属? Jakarta的commons目Q目前的版本?.0RC2。commons下本来已l有一个net的子目Q但是又把httpclient单独提出 来,可见http服务器的讉Kl非易事?/span>
Commons-httpclient 目是专门设计来简化HTTP客户端与服务器进行各U通讯~程。通过它可以让原来很头疼的事情现在L的解冻I例如你不再管是HTTP或者HTTPS? 通讯方式Q告诉它你想使用HTTPS方式Q剩下的事情交给httpclient替你完成。本文会针对我们在编写HTTP客户端程序时l常到的几个问题进 行分别介l如何用httpclient来解军_们,Z让读者更快的熟悉q个目我们最开始先l出一个简单的例子来读取一个网늚内容Q然后@序渐q解 x前进中的所有问题?/span>
1Q?d|页(HTTP/HTTPS)内容
下面是我们给出的一个简单的例子用来讉K某个面
/*
* Created on 2003-12-14 by Liudong
*/
package http.demo;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;
/**
* 最单的HTTP客户?用来演示通过GET或者POST方式讉K某个面
* @author Liudong
*/
public class SimpleClient {
public static void main(String[] args) throws IOException
{
HttpClient client = new HttpClient();
//讄代理服务器地址和端?nbsp;
//client.getHostConfiguration().setProxy("proxy_host_addr",proxy_port);
//使用GETҎQ如果服务器需要通过HTTPSq接Q那只需要将下面URL中的http换成https
HttpMethod method = new GetMethod("http://java.sun.com");
//使用POSTҎ
//HttpMethod method = new PostMethod("http://java.sun.com");
client.executeMethod(method);
//打印服务器返回的状?/span>
System.out.println(method.getStatusLine());
//打印q回的信?/span>
System.out.println(method.getResponseBodyAsString());
//释放q接
method.releaseConnection();
}
}
在这个例子中首先创徏一? HTTP客户?HttpClient)的实例,然后选择提交的方法是GET或者POSTQ最后在HttpClient实例上执行提交的ҎQ最后从所? 择的提交Ҏ中读取服务器反馈回来的结果。这是使用HttpClient的基本流E。其实用一行代码也可以搞定整个请求的q程Q非常的单!
2Q?以GET或者POST方式向网|交参?/p>
其实前面一个最单的CZ中我们已l介l了如何使用GET或者POST方式来请求一个页面,本小节与之不同的是多了提交时讑֮面所需的参敎ͼ我们 知道如果是GET的请求方式,那么所有参数都直接攑ֈ面的URL后面用问号与面地址隔开Q每个参数用&隔开Q例如:http://java.sun.com?name=liudong&mobile=123456Q但是当使用POSTҎ时就会稍微有一点点ȝ。本节的例子演C向如何查询手机L所在的城市Q代码如下:
/*
* Created on 2003-12-7 by Liudong
*/
package http.demo;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;
/**
* 提交参数演示
* 该程序连接到一个用于查询手机号码所属地的页?/p>
* 以便查询LD?330227所在的省䆾以及城市
* @author Liudong
*/
public class SimpleHttpClient {
public static void main(String[] args) throws IOException
{
HttpClient client = new HttpClient();
client.getHostConfiguration().setHost("www.imobile.com.cn", 80, "http");
HttpMethod method = getPostMethod();//使用POST方式提交数据
client.executeMethod(method);
//打印服务器返回的状?/p>
System.out.println(method.getStatusLine());
//打印l果面
String response =
new String(method.getResponseBodyAsString().getBytes("8859_1"));
//打印q回的信?/p>
System.out.println(response);
method.releaseConnection();
}
/**
* 使用GET方式提交数据
* @return
*/
private static HttpMethod getGetMethod(){
return new GetMethod("/simcard.php?simcard=1330227");
}
/**
* 使用POST方式提交数据
* @return
*/
private static HttpMethod getPostMethod(){
PostMethod post = new PostMethod("/simcard.php");
NamePair simcard = new NamePair("simcard","1330227");
post.setRequestBody(new NamePair[] { simcard});
return post;
}
}
在上面的例子中页?a target="_blank">http://www.imobile.com.cn/simcard.php需要一个参数是simcardQ这个参数gؓ手机LD,x机号码的前七位,服务器会q回提交的手机号码对应的省䆾、城市以及其他详l信息。GET的提交方法只需要在URL后加入参C息,而POST则需要通过NamePaircL讄参数名称和它所对应的?/p>
3Q?处理面重定?/p>
在JSP/Servlet~程中response.sendRedirectҎ是使用HTTP协议中的重定向机制。它与JSP中的< jsp:forward …>的区别在于后者是在服务器中实现页面的跌{Q也是说应用容器加载了所要蟩转的面的内容ƈq回l客LQ而前者是q回一个状态码Q这些状态码 的可能D下表Q然后客Ld需要蟩转到的页面的URLq新加载新的页面。就是这样一个过E,所以我们编E的时候就要通过 HttpMethod.getStatusCode()Ҏ判断q回值是否ؓ下表中的某个值来判断是否需要蟩转。如果已l确认需要进行页面蟩转了Q那么可 以通过dHTTP头中的location属性来获取新的地址?/p>
import java.security.*;
import java.io.*;
import java.util.*;
import java.security.*;
import java.security.cert.*;
import sun.security.x509.*
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
?从文件中d证书
用keytool?keystore中的证书写入文g中,然后从该文g中读取证书信?br />
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in=new FileInputStream("out.csr");
Certificate c=cf.generateCertificate(in); String s=c.toString();
?从密钥库中直接读取证?/strong>
String pass="123456";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,pass.toCharArray());
java.security.cert.Certificate c=ks.getCertificate(alias);//alias为条目的别名
?JAVAE序中显C书指定信?/strong>
System.out.println("输出证书信息:"n"+c.toString());
System.out.println("版本?"+t.getVersion());
System.out.println("序列?"+t.getSerialNumber().toString(16));
System.out.println("M名:"+t.getSubjectDN());
System.out.println("{֏者:"+t.getIssuerDN());
System.out.println("有效期:"+t.getNotBefore());
System.out.println("{法Q?+t.getSigAlgName());
byte [] sig=t.getSignature();//{?br />
PublicKey pk=t.getPublicKey();
byte [] pkenc=pk.getEncoded();
System.out.println("公钥");
for(int i=0;i<pkenc.length;i++)System.out.print(pkenc[i]+",");
?JAVAE序列出密钥库所有条?/strong>
String pass="123456";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,pass.toCharArray());
Enumeration e=ks.aliases();
while(e.hasMoreElements())
java.security.cert.Certificate c=ks.getCertificate((String)e.nextElement());
?JAVAE序修改密钥库口?/strong>
String oldpass="123456";
String newpass="654321";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,oldpass.toCharArray());
in.close();
FileOutputStream output=new FileOutputStream(".keystore");
ks.store(output,newpass.toCharArray());
output.close();
?JAVAE序修改密钥库条目的口o及添加条?/strong>
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
Certificate [] cchain=ks.getCertificate(alias);获取别名对应条目的证书链
PrivateKey pk=(PrivateKey)ks.getKey(alias,oldkeypass.toCharArray());获取别名对应条目的私?br />
ks.setKeyEntry(alias,pk,newkeypass.toCharArray(),cchain);向密钥库中添加条?br />
W一个参数指定所d条目的别名,假如使用已存在别名将覆盖已存在条目,使用新别名将增加一个新条目Q第二个参数为条目的U钥Q第三个|的新口令,W四个ؓ该私钥的公钥的证书链
FileOutputStream output=new FileOutputStream("another");
ks.store(output,storepass.toCharArray())keystore对象内容写入新文?/p>
?JAVAE序验别名和删除条目
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
ks.containsAlias("sage");验条目是否在密钥库中Q存在返回true
ks.deleteEntry("sage");删除别名对应的条?br />
FileOutputStream output=new FileOutputStream(".keystore");
ks.store(output,storepass.toCharArray())keystore对象内容写入文g,条目删除成功
?JAVAE序{֏数字证书
Q?Q从密钥库中dCA的证?br />
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
java.security.cert.Certificate c1=ks.getCertificate("caroot");
Q?Q从密钥库中dCA的私?br />
PrivateKey caprk=(PrivateKey)ks.getKey(alias,cakeypass.toCharArray());
Q?Q从CA的证书中提取{֏者的信息
byte[] encod1=c1.getEncoded(); 提取CA证书的编?br />
X509CertImpl cimp1=new X509CertImpl(encod1); 用该~码创徏X509CertImplcd对象
X509CertInfo cinfo1=(X509CertInfo)cimp1.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); 获取X509CertInfo对象
X500Name issuer=(X500Name)cinfo1.get(X509CertInfo.SUBJECT+"."+CertificateIssuerName.DN_NAME); 获取X509Namecd的签发者信?br />
Q?Q获取待{֏的证?br />
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in2=new FileInputStream("user.csr");
java.security.cert.Certificate c2=cf.generateCertificate(in);
Q?Q从待签发的证书中提取证书信?br />
byte [] encod2=c2.getEncoded();
X509CertImpl cimp2=new X509CertImpl(encod2); 用该~码创徏X509CertImplcd对象
X509CertInfo cinfo2=(X509CertInfo)cimp2.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); 获取X509CertInfo对象
Q?Q设|新证书有效?br />
Date begindate=new Date(); 获取当前旉
Date enddate=new Date(begindate.getTime()+3000*24*60*60*1000L); 有效期ؓ3000?br />
CertificateValidity cv=new CertificateValidity(begindate,enddate); 创徏对象
cinfo2.set(X509CertInfo.VALIDITY,cv); 讄有效?br />
Q?Q设|新证书序列?br />
int sn=(int)(begindate.getTime()/1000); 以当前时间ؓ序列?br />
CertificateSerialNumber csn=new CertificateSerialNumber(sn);
cinfo2.set(X509CertInfo.SERIAL_NUMBER,csn);
Q?Q设|新证书{֏?br />
cinfo2.set(X509CertInfo.ISSUER+"."+CertificateIssuerName.DN_NAME,issuer);应用W三步的l果
Q?Q设|新证书{法信息
AlgorithmId algorithm=new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
cinfo2.set(CertificateAlgorithmId.NAME+"."+CertificateAlgorithmId.ALGORITHM,algorithm);
Q?0Q创书ƈ使用CA的私钥对其签?br />
X509CertImpl newcert=new X509CertImpl(cinfo2);
newcert.sign(caprk,"MD5WithRSA"); 使用CAU钥对其{
Q?1Q将新证书写入密钥库
ks.setCertificateEntry("lf_signed",newcert);
FileOutputStream out=new FileOutputStream("newstore");
ks.store(out,"newpass".toCharArray()); q里是写入了新的密钥库,也可以用第七条来增加条?/p>
?数字证书的检?/strong>
Q?Q验证证书的有效?br />
QaQ获取X509Certificatecd对象
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in1=new FileInputStream("aa.crt");
java.security.cert.Certificate c1=cf.generateCertificate(in1);
X509Certificate t=(X509Certificate)c1;
in2.close();
QbQ获取日?br />
Date TimeNow=new Date();
QcQ检验有效?br />
try{
t.checkValidity(TimeNow);
System.out.println("OK");
}catch(CertificateExpiredException e){ //q期
System.out.println("Expired");
System.out.println(e.getMessage());
}catch((CertificateNotYetValidException e){ //未生效
System.out.println("Too early");
System.out.println(e.getMessage());}
Q?Q验证证书签名的有效?br />
QaQ获取CA证书
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in2=new FileInputStream("caroot.crt");
java.security.cert.Certificate cac=cf.generateCertificate(in2);
in2.close();
QcQ获取CA的公?br />
PublicKey pbk=cac.getPublicKey();
QbQ获取待验的证书Q上步已l获取了Q就是C1Q?br />
QcQ检验证?br />
boolean pass=false;
try{
c1.verify(pbk);
pass=true;
}catch(Exception e){
pass=false;
System.out.println(e);
}
使用BC Provider在Java中构造一个证书请求格式的对象调用其构造函数即?q个函数如下:
PKCS10CertificationRequest(java.lang.String signatureAlgorithm, X509Name subject, java.security.PublicKey key, ASN1Set attributes, java.security.PrivateKey signingKey)
它的参数是自{法,证书甌者的DN,证书甌者的公钥,额外的属性集(是要申L证书的扩展信?,甌证书者的U钥.甌证书者的U钥仅仅是用来进行一下自{,q不出现在证书请求中,需要自{的目的是保证该公钥确实ؓ甌者所?
调用该对象的getEncoded()Ҏ可以其q行DER~码,然后储存h,该对象还有另一个构造函?
PKCS10CertificationRequest(byte[] bytes)
q个构造函数的作用是直接从储存的DER~码中把q个对象q原出来.
利用证书hl构q行证书{֏的代码如?q里假设CSR是一个已l获取出来的PKCS10CertificationRequestl构:
PublicKey SubjectPublicKey = CSR.getPublicKey();
CertificationRequestInfo CSRInfo = CSR.getCertificationRequestInfo();
X509Name SubjectDN = CSRInfo.getSubject();
ASN1Set Attributes = CSRInfo.getAttributes();
q样,甌者的MDN,甌者的公钥,甌者希望在证书扩展信息中填写的属性都得到?剩下的事情就和用户在现场输入时一样了,其它的信息一般是甌? 不能军_?另外证书h格式中有一样信息没有明给出来,那就是证书的有效?q个应该单独询问用户,或者用其它的方法保存v?
一个TBS中包含了以下q些主要信息:
证书的版?通常?(X509v3)
证书的序列号,RFC3280中规?每个CA必须保它颁发的每一份证书的序列号都是唯一?q且序列号只能用非负整?
{֏?CA)的主体名U?一个DN对象.
证书的有效期,表示Ҏ是两个时间?表示了该证书从何时开始生?何时开始作?
待认证的M的主体名U?也是一个DN对象.
待认证的M的公?M安全应用在确认完证书的有效性后,可以开始用该M的公钥与之进行安全通信.
如果是X509v3证书,即版本号?的话,后面q有一个证书扩展信息字D?可以在证书里面添加一些其它的信息.
下面我们来看一下表CZ体的M名称l构:DN.q个l就构是一个属性的集合.每个属性有属性名U和属性?它的作用是用来表示"我是?,也就是说,q个证书到底是谁颁发l谁?q个证书对应的公钥是谁拥有的.
通常使用一个字W串来表CDNl构,q种字符串说明了q种l构中的各个属性的cd和?
C=CN;S=BeiJing;L=BeiJing;O=PKU;OU=ICST;CN=wolfenstein
q里C是国家和地区代码,S和L都是地区代码,S相当于省或者州q样的?L相当于城市?O是组l机构名U?OU是次U组l机构名U?CN是主体的 通用?common name).在这?C,S,L{等属性的cd都是相对固定?例如C一般就是用来表C国家和地区代码,在DNl构中还可以d一些其它类型的信息,一? 也都是以"xxx=xxx"q样来表C的.
下面我们来说明如何在Java语言中构造出一个主体名U对?
BC Provider中用X509Name对象来表CDN,构造一个X509Name的步骤大体如?
先构造两个vector对象,用来表示属性名U和属性?
Vector oids = new Vector();
Vector attributes = new Vector();
然后在oidsq个用来表示属性名U的vector对象中将属性名UC个一个添加进?
oids.addElement(X509Name.C);
......
oids.addElement(X509Name.CN);
X509Name对象里面有若q常?例如上面的X509Name.C.q有X509Name.ST{等,都可以和上面丄例子对应h.
然后属性值添加到attributes对象?
attributes.addElement("CN");
......
attributes.addElement("Wolfenstein");
最后就可以构造出一个X509Name对象:
X509Name SubjectDN = new X509Name(oids, attributes);
q样,我们的主体名U结构就立h?
下次我们可以讲关键的部分了,那就是如何用JavaE序完成CA最重要的功?{v证书.
然后我们可以开始开CA?首先,作ؓ一个CA要有自己的一对公钥和U钥,我们先要生成q么一?使用KeyPairGenerator对象可以了, 调用KeyPairGenerator.getInstanceҎ可以Ҏ要生成的密钥cd来生一个合适的实例,例如常用的RSA,DSA{?然后? 用该对象的initializeҎ和generateKeyPairҎ可以生一个KeyPair对象?然后调用KeyPair对象中的相应Ҏ 可以获取生成的密钥对中的公钥和U钥?
有了公钥和私钥对以后,下面的一个很现实问题是如何把它们储存v?通常我们要对q些安全对象,如公?U钥,证书{先q行~码.~码的目的是Z把结 构复杂的安全对象变成字节以便存储和d,如DER~码.另外,通常q把DER~码后的字节再ơ进行base64~码,以便使字节流中所有的字节都变 成可打印的字?
在Java语言?q些安全对象基本上都有getEncoded()Ҏ.例如:
byte[] keyBytes = privateKey.getEncoded();
q样把一个私钥进行了DER~码后的l果保存C个byte数组中了.然后可以把q个byte数组保存CQ何介质中.如果有必要的?可以使用BC Provider中的Base64~码解码器类q行~码,像q样:
byte data[] = Base64.encode(keyBytes);
要从文g中读取私钥则应该q样:
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyData);
KeyFactory kfac = KeyFactory.getInstance("RSA");
privateKey = kfac.generatePrivate(spec);
q里说明一?对RSAU钥q行~码׃自动地按照PKCS8q行.因此d的时候将包含~码的字节数l作为PKCS8EncodedKeySpec对象 的构造函数的参数可以生成一个该cd的对?然后创徏一个密钥工厂对象就可以按照要求生成一个RSAU钥?很显然这里的keyData应该是和上面? keyBytes内容相同.
Z提高pȝ的安全?通常U钥在经qDER~码?q会使用一个口令进行加?然后再储存在文gpȝ?在用私钥的时?如果没有正确的口?是无法把U钥q原出来?
保存证书和保存私钥类?Certificate对象中也有一个getEncoded的方?
q次pq些.大家应该可以把这些安全对象很熟练C文gpȝ和内存之间来回地折腾了吧.q对以后实现CA是很重要?下次我会讲一下证书中表示M的方?DN.q次先写q么多了,下次开始具体讲把这些东西搞下来后怎么开始干zd.我以我现在手头上正在做的事情告诉大家,如何做一个CA.
有了BC Provider,开CA,真的是q么?
服务端应?#8216;服务端私?#8217;?#8216;客户端公?#8217;与客L通讯Q客L应用‘客户端私?#8217;?#8216;服务端公?#8217;与服务端通讯?/p>
如下介绍证书生成Q一套)Q?/span>
Q、用JDK的keytool生成密钥store
keytool -genkey -alias mykey -keystore srvstore
输入密码Q程序中密码?#8216;123456’Q和相应的证书信?/p>
Q、从srvstore中导?/span>
keytool -export -alias mykey -file srvcert.crt -keystore srvstore
输入刚才上面讄的密?/p>
Q、将证书导到公钥?/span>
keytool -import -alias mytrust -file srvcert.crt -keystore srvtrust
输入公钥的密码(E序中密码ؓ‘123456’Q?/p>
<Service name="Catalina">
<Connector URIEncoding="UTF-8"
acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true"
port="8080" redirectPort="8443"
maxSpareThreads="75" maxThreads="150" minSpareThreads="25">
</Connector>
<Connector URIEncoding="UTF-8" port="8443"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" debug="0" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS"
keystoreFile="${catalina.home}/conf/ssl-test.net-tomcat.keystore"
keystorePass="openssl"
truststoreFile="${catalina.home}/conf/ssl-test.net-tomcat.keystore"
truststorePass="openssl"/>
......
auth-method=CLIENT-CERT
说明?以客L数字证书来确认用Lw䆾", transport-guarantee=CONFIDENTIAL
表示应用E序要求数据必须在一U?防止其他实体看到传输的内容的方式中传?.<login-config>
<!-- Authorization setting for SSL -->
<auth-method>CLIENT-CERT</auth-method>
<realm-name>Client Cert Users-only Area</realm-name>
</login-config>
<security-constraint>
<!-- Authorization setting for SSL -->
<web-resource-collection >
<web-resource-name >SSL</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
request.getAttribute("javax.servlet.request.X509Certificate")
可以得?https h的客L证书链信? 其中W一个元素就是客L证书, 相应的示例代码如? String certSubject = null; |
1QTomcat?/span>
Tomcat
是Apache
Jakarta的子目之一Q作Z个优U的开源web应用服务器,全面支持jsp1.2以及servlet2.3规范。因其技术先q、性能E_Q而且?
费,因而深受Java爱好者的喜爱q得C部分软g开发商的认可,成ؓ目前比较行的web应用服务器?/p>
2QSSL(Server Socket Layer)?/span>
?
|络上信息在?宿的传递过E中会经q其它的计算机。一般情况下Q中间的计算Z会监听\q的信息。但在用网上银行或者进行信用卡交易的时候有可能被监
视,从而导致个人隐U的泄露。由于Internet和Intranet体系l构的原因,L某些够读取ƈ替换用户发出的信息。随着|上支付的不断发
展,Z对信息安全的要求来高。因此Netscape公司提出了SSL协议Q旨在达到在开攄l?Internet)上安全保密地传输信息的目的,q?
U协议在WEB上获得了q泛的应用。之后IETF(www.ietf.org)对SSL作了标准化,即RFC2246Qƈ其UCؓTLS
QTransport Layer SecurityQ,从技术上ԌTLS1.0与SSL3.0的差别非常微?
3QSSL工作原理
SSL协议使用不对U加密技术实C话双方之间信息的安全传递。可以实C息传递的保密性、完整性,q且会话双方能鉴别对方n份。不同于常用的http协议Q我们在与网站徏立SSL安全q接时用https协议Q即采用https://ip:port/的方式来讉K?/p>
当我们与一个网站徏立httpsq接Ӟ我们的浏览器与Web Server之间要经q一个握手的q程来完成n份鉴定与密钥交换Q从而徏立安全连接。具体过E如下:
4.1 用到的Y件包
以上工具的安装过E可以参考自带的帮助Q本文就不再详细描述了?/p>
4.2 建立自己的CA
4.2.1 建立工作目录
mkdir ca
4.2.2 生成CAU钥以及自签名根证书
4.2.2.1 生成CAU钥
openssl genrsa -out ca"ca-key.pem 1024
4.2.2.2 生成待签名证?/strong>
openssl req -new -out ca"ca-req.csr -key ca"ca-key.pem
4.2.2.3 用CAU钥q行自签?/strong>
openssl x509 -req -in ca"ca-req.csr -out ca"ca-cert.pem -signkey ca"ca-key.pem -days 365
4.3 讄Tomcat 4.x
在本文中用符?%JDK_HOME%"来表CJDK的安装位|,用符?%TCAT_HOME%" 表示Tomcat的安装位|?/p>
4.3.1建立工作目录
mkdir server
4.3.2 生成server端证?/strong>
4.3.2.1 生成KeyPair
%JDK_HOME%"bin"keytool
-genkey -alias tomcat_server -validity 365 -keyalg RSA -keysize 1024
-keypass changeit -storepass changeit -dname "cn=localhost,
ou=department, o=company, l=Beijing, st=Beijing, c=CN" -keystore
server"server_keystore
4.3.2.2 生成待签名证?/strong>
%JDK_HOME%"bin"keytool -certreq
-alias tomcat_server -sigalg MD5withRSA -file server"server.csr
-keypass changeit -keystore server"server_keystore -storepass changeit
4.3.2.3 用CAU钥q行{
openssl x509 -req -in server"server.csr -out server"server-cert.pem -CA ca"ca-cert.pem -CAkey ca"ca-key.pem -days 365
4.3.2.4 导入信Q的CA根证书到JSSE的默认位|?%JDK_ROOT %/jre/security/cacerts)
%JDK_HOME%"bin"keytool
-import -v -trustcacerts -storepass changeit -alias my_ca_root -file
ca"ca-cert.pem -keystore %JDK_HOME%"jre"lib"security"cacerts
4.3.2.5 把CA{后的server端证书导入keystore
%JDK_HOME%"bin"keytool
-import -v -trustcacerts -storepass changeit -alias tomcat_server -file
server"server-cert.pem -keystore server"server_keystore
4.3.2.6 查看server端证?/strong>
keytool -list -keystore %JDK_HOME%"jre"lib"security"cacerts
keytool -list -keystore server"server_keystore
4.3.3 修改server.xml使Tomcat支持SSL
首先扑ֈ以下内容Q去掉对其的注释。然后参照红色部分修攏V如果配|Tomcat不验证客戯n份,可以讄clientAuth="false"?
<Connector className="org.apache.catalina.connector.http.HttpConnector" |
4.4 在IE中安装个?/strong>
4.4.1 建立工作目录
mkdir client
4.4.2 生成clientU钥q用CAU钥{
4.4.2.1 生成clientU钥
openssl genrsa -out client"client-key.pem 1024
4.4.2.2 生成待签名证?/strong>
openssl req -new -out client"client-req.csr -key client"client-key.pem
4.4.2.3 用CAU钥q行{
openssl x509 -req -in
client"client-req.csr -out client"client.crt -signkey
client"client-key.pem -CA ca"ca-cert.pem -CAkey ca"ca-key.pem
-CAcreateserial -days 365
4.4.2.4 生成client端的个h证书
因ؓJSSE1.0.2没有完全实现了对PKCS#12格式文g的操?只能dQ不能输?Q所以在q里需要用openssl制作client端的个h证书(包含U钥)?br />
openssl pkcs12 -export -clcerts -in client"client.crt -inkey client"client-key.pem -out client"client.p12
4.4.2.5 安装信Q的根证书
把ca"ca-key.pem改名为ca"ca-key.cerQ在client端的IE中?工具 ' Internet选项 ' 内容 ' 证书 ' 导入"把我们生成的CA根证书导入,使其成ؓ用户信Q的CA?/p>
4.4.3 安装个h证书
把client.p12导入到client端的IE中作Z书,导入q程?.4.2.5?/p>
4.5 用IE览器用SSL协议讉KTomcat
4.5.1 启动Tomcat 4.x
执行%TCAT_HOME%"bin"startup.bat启动Tomcat 4.x
4.5.2 用IE讉KTomcat 4.x
在IE览器的地址栏中输入https://localhost:8443Q如果前面的操作都正的话,应该可以看到Tomcat的欢q页面。同时状态栏上的锁处于闭合状态,表示您已l成功地与服务器建立了要求客L验证的SSL安全q接?/p>
5 l论
?
上我们实C为Tomcat 4.x配置要求客户端验证的SSL的全q程。对于其它类型的服务器,例如ApacheQNetscape
Enterprise Server,
WebsphereQWeblogic{,一般只是在服务器端保存证书的方式略有不同,但它们的原理都是cM的,配置时可以在本文中办法的基础上做出相?
的调整?/p>