4/14/2009

Hibernate运行报错:org.hibernate.TransactionException: JDBC rollback failed

现象:
系统刚刚部署好的当天,访问系统没有问题,运行一段时间后访问该系统便会报错:org.hibernate.TransactionException: JDBC rollback failed,这个时候再重启tomcat一切又都好用了。也可以通过下面的方法测试,重新启动tomcat,一切好用,运行一段时间后,继续报错…服务器控制台提示:关闭的连接。
页面提示错误:

org.hibernate.TransactionException: JDBC rollback failed org.hibernate.transaction.JDBCTransaction.rollback(JDBCTransaction.java:150) com.hostel.guest.Base.BaseDAO.save(BaseDAO.java:646) com.hostel.guest.Base.BaseLogDAO.savesysloginfo(BaseLogDAO.java:199) com.hostel.guest.servlet.AdminLogin.service(AdminLogin.java:99) javax.servlet.http.HttpServlet.service(HttpServlet.java:810) com.hostel.guest.servlet.MainServlet.service(MainServlet.java:586) javax.servlet.http.HttpServlet.service(HttpServlet.java:810) org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)控制台错误:

org.hibernate.TransactionException: JDBC rollback failed
at org.hibernate.transaction.JDBCTransaction.rollback(JDBCTransaction.java:150)
at com.hostel.guest.Base.BaseDAO.save(BaseDAO.java:646)
at com.hostel.guest.Base.BaseLogDAO.savesysloginfo(BaseLogDAO.java:199)
at com.hostel.guest.servlet.AdminLogin.service(AdminLogin.java:99)
org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664)
at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
at org.apache.tomcat.util.net.MasterSlaveWorkerThread.run(MasterSlaveWorkerThread.java:112)
at java.lang.Thread.run(Thread.java:595)
Caused by: java.sql.SQLException: 关闭的连接

网上的解决方法如下:
方法一:上网查了一下,有人说是数据库事务的原因,要把hibernate的autocommit设为false,我找了一下这个好像要在代码里面设置,而不能通过简单的设置Hibernate的配置文件来设置,就没有继续走这条路。后来想到可能是数据库连接池的原因。
方法二:在hibernate配置文件中加入对c3p0连接池的配置,同时将c3p0-0.8.4.5.jar拷贝到应用的lib文件夹下。这里说明一下,如果没有这个配置,hibernate默认使用其自身的数据库连接池。配置后则采用第三方的c3p0连接池。
使用c3p0连接池后确实没有问题了,但是我还是不明白为什么使用默认的连接池会有问题,如果哪位大哥清楚麻烦回复一下啊,谢谢!

把Hibernate的connection.autocommit设置为false

出现这个问题的原因
首先, 你的代码是从数据库读取用户信息, 不是写入数据. 所以不能使用事务和rollback方法.
第二, 当你更新和插入一条记录到数据库, 使用事务的时候, 关闭数据库的自动提交. 检查你的连接池是否正常.
第三, 当你有一个错误, 一定要关闭你的Hibernate连接. 当你捕获一个异常, 使用finally关闭hibernate Session或者数据库连接.
另外, 可能连接池已经用完.

关于JAVA中public protected private static四个关键字

1、前三个是变量访问控制符
public 对无论任何类都可见
protected只对子类可见
private只对该类可见
static是修饰方法的,定义成static的方法则不是当前类的方法,不可以通过类名.方法名访问

public 还修饰类名
如果没有修饰符则只对包内可见,有public则没有这个限制

public 代表任何类都可以访问
private 代表只有类本身才可以访问
protected 在同一个包中,类及其子类可以访问
不加关键字,默认的是包的访问权限

static是静态的意思,静态的类或者变量只创建一次;不用创建任何实例就可以访问static的东西;

1、public 公有成员函数可被任何其它对象和类的成员函数调用。 当该成员函数必须被该函数所在的层次结构之外的其他对象和类在访问时。
2、protected 被保护的成员函数可被它所在的类或该类的子类的任何成员函数调用。 当该成员函数提供的行为被它所在类的层次结构内部而非外部需要时。
3、private 私有成员函数只可以被该类所在的其它成员函数调用,该类的子类不可以调用。 当该成员函数所提供的行为明确针对定义它的类时。私有成员函数常常是重新分配要素的结果。重新分配要素又叫“重组”,指类内其它成员函数封装某一个特定行为的做法。
4、 static关键字
通常,我们创建类时会指出那个类的对象的外观与行为。除非用new创建那个类的一个对象,否则实际上并未得到任何东西。只有执行了new后,才会正式生成数据存储空间,并可使用相应的方法。
但在两种特殊的情形下,上述方法并不堪用。一种情形是只想用一个存储区域来保存一个特定的数据——无论要创建多少个对象,甚至根本不创建对象。另一种情形是我们需要一个特殊的方法,它没有与这个类的任何对象关联。也就是说,即使没有创建对象,也需要一个能调用的方法。为满足这两方面的要求,可使用static(静态)关键字。一旦将什么东西设为static,数据或方法就不会同那个类的任何对象实例联系到一起。所以尽管从未创建那个类的一个对象,仍能调用一个static方法,或访问一些static数据。而在这之前,对于非static数据和方法,我们必须创建一个对象,并用那个对象访问数据或方法。这是由于非static数据和方法必须知道它们操作的具体对象。当然,在正式使用前,由于static方法不需要创建任何对象,所以它们不可简单地调用其他那些成员,同时不引用一个已命名的对象,从而直接访问非static成员或方法(因为非static成员和方法必须同一个特定的对象关联到一起)。
有些面向对象的语言使用了“类数据”和“类方法”这两个术语。它们意味着数据和方法只是为作为一个整体的类而存在的,并不是为那个类的任何特定对象。有时,您会在其他一些Java书刊里发现这样的称呼。
为了将数据成员或方法设为static,只需在定义前置和这个关键字即可。例如,下述代码能生成一个static数据成员,并对其初始化:

class StaticTest {
Static int i = 47;
}

用 LotusScript 实现 Excel 报表的自动生成和操作

Lotus Notes 是 Windows® 和 Macintosh® 计算机上功能强大且用途广泛的软件,在企业内部的办公应用领域,扮演了十分重要的角色。而 Excel® 表格也是企业办公领域的流行工具之一,经常作为统计数据的报表使用。Lotus Domino Designer 当中的 LotusScript 语言提供了丰富的编程功能,可以在 Notes 数据库当中实现对 Excel 文件的自动生成、操作和发送等等功能。本文结合本人在开发过程当中的实际经验,以一个具体的应用模块为例,介绍在 Lotus 平台下,用 LotusScript 语言实现 Excel 报表操作功能的原理、方法和一些实用技巧。
Lotus Notes 应用开发概念简介
Lotus Notes 是大型企业内部办公协作的首选工具之一,除了众所周知的电子邮件收发功能以外,Notes 也是一个强大的应用程序运行平台,可以通过运行各种 Notes 应用来实现各种各样的功能。Notes 客户端上运行的应用被称为 Notes 数据库,如同网页浏览器(如Firefox)通过打开不同的 URL 访问功能各异的网络应用(网页)一样,Notes 客户端的强大功能,正在于通过打开不同的 Notes 数据库,从而实现千变万化的业务功能。从物理上看,Notes 数据库很简单,就是一个后缀名为“nsf”的文件,Notes 客户端只要打开本地或者远程服务器上的 nsf 文件,就可以访问这个 Notes 数据库了。从逻辑上看,Notes 数据库文件将数据库的设计和数据都集中在一起,便于访问和维护。
要理解 Notes 数据库的逻辑结构,就要理解 Notes 数据库独特的数据库类型-“文档数据库”。一提到“数据库”,人们都会想起通用的关系型数据库,其实,很多其他形式的数据库,如“文档数据库”也非常的有价值,便于实现某些特定的功能。文档数据库的概念很简单,就是在数据库当中没有关系型数据库那样复杂的结构和严格的规定,数据按照简单的“文档”形式来存储,一个“文档”当中包含很多内容字段,文档之间的关联不是很紧密。除开数据存储以外,Notes 数据库当中还储存了一些操作数据的应用程序,包括脚本程序、图形界面设计、数据表单设计等等。有了这些设计,Notes 数据库就不仅仅具有简单的数据存储功能,还可以实现丰富的应用逻辑功能了。关系型数据库虽然很通用,可以存储任何结构的数据,但是在存储结构不是很复杂,数据间关联不是很紧密的情况下,文档数据库的形式就更加容易设计和修改;而这种将数据和设计放在一个文件当中的形式,也更加便于部署和掌握。
一个很容易想到的文档数据库的例子就是电子邮件信箱:在邮箱里,每一封邮件都可以被认为是一个独立的文档,有着类似的结构和字段,而且相互之间关联程度不大。如果用关系型数据库来记录邮件内容,在开发过程中必须遵循很多的限制和约束,而且对于归类、排序、自动设置属性等等邮件文档的常用功能,必须按照关系型数据库的规范编写比较复杂的应用程序来实现。有了文档数据库,事情就变得简单了,由于文档的共同特性,文档数据库一般都有通用的归类、排序等功能,几乎不用写代码,就可以实现这些常规的操作功能。而且,当我们要迁移邮箱内容的时候,只要拷贝邮箱的 nsf 文件就可以了,不会像关系数据库那样需要复杂的导入导出和配置过程。所以,Lotus Notes 当中应用最为广泛的,就是邮件数据库,它充分体现了文档数据库的特点,另外,个人通讯录、日程安排管理、公司内部的文档管理系统等等企业办公协作应用,也可以用文档数据库很好的实现。

Domino系统登录使用数据库::定制OA登录界面

下载后解压到domino/data目录下即可;
可根据自己的需要重新修改;
下载数据库

4/02/2009

img控件判断一个图片是否存在的方法[不错的js代码]

,img src=”images/img.jpg” mce_src=”images/img.jpg” onerror=”this.src=’默认图.jpg’”>

当指定的图片不存在时用,默认图替换该图片


'SCRIPT>
var sImg=’’;
function fnLoadFirst(){
oContainer.innerHTML=sImg;
oStub.onerror=fnLoadFail1;
oStub.src=”" mce_src=”";
oStub.style.display=”block”;
}
function fnLoadFail1(){
oStub.alt=”Image failed to load.”;
return true;
}
'/SCRIPT>

'INPUT TYPE=button VALUE=”Load First Image” onclick=”fnLoadFirst()”>
'DIV ID=oContainer>'/DIV>


参考以上代码,实际上只要在客户端定义的onerror事件进行处理就行了。
imgID.onerror = “imgID.src=’出错图片.gif’”

通过js取select的text值

通过JS代码获得select的文本值。document.all.name.value是获得value中的值;

'script language=”javascript”>
function chk(){
var obj = document.getElementById(”sel”);
var strsel = obj.options[obj.selectedIndex].text;
if (confirm(”确定要选择”+strsel+”吗?”)){

}else{

}
}
'/script>
'select name=”sel” onchange=”return chk()”>



'/select>

通过js判断当前页面中input是否存在

在bs开发中经常会判断当前页面中input 是否存在..

下面代码可解决这个问题:

var oFileChecker = document.getElementById(”fileChecker”);
if (oFileChecker!=null){

//input存在…

}

说明:

此处必须使用getElementById来获得,通过getElementByName来获得,可以判断是否存在,但进一步取值时,obj.value则提示为undefined

3/31/2009

窗口控制:让窗口最大化没有滚动条,真正的全屏

问: 窗口最大化没有滚动条,让后按Esc键撤出最大化,有一个默认值200*200, 默认值有滚动条,有菜单,有标准按钮 _____________答1: 这样? 'body onkeydown=if(event.keyCode==27)resize()> 'div style=width:1000;height:1000>'object id=hhctrl classid=”clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11″> 'param name=”Command” value=”Maximize”> '/object>按ESC切换最大化'/div> 'script> function resize(){ hhctrl.Click() document.body.scroll=(document.body.scroll!=”no”?”no”:”") } window.resizeTo(200,200); '/script>

'span id="more-128">'/span>

______________________________________________________________________________ 答2: 请问一下classid=”clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11″ 是那个控件,您能帮我列一下常用控件的编号吗?万分感谢。 ______________________________________________________________________________ 答3: 我只知道这样可以没有滚动条 'body scroll=no> ______________________________________________________________________________ 答4: window.open(”index.htm”, “”, “fullscreen=yes,scrollbars=no”);

java中把汉字转换为拼音

最近在项目中有个需求,要求按照同音查询,在网上搜了段代码,可以实现此功能:把汉字转换为拼音,如:麦子—》maizi

此代码在windows平台上没问题,实现原理可以自己去看代码;

代码下载

但此代码在linux平台上就遇到个问题

byte[] bytes = (String.valueOf(cn)).getBytes();
if (bytes == null || bytes.length > 2 || bytes.length <= 0)
{ // 错误
return 0;
}

在linux环境下,此处return 0,导致不能进行转换;

初步猜想:问题可能出在系统字符集上;

3/29/2009

【转载】单点登录的简单实现

在门户项目中,经常会遇到如何实现单点登录的问题,下面就本人的经验做个总结。欢迎大家进行补充讨论。

单点登录的具体实现有很多种选择,包括:
采用专门的SSO商业软件: 主要有:Netgrity的Siteminder,已经被CA收购。Novell 公司的iChain。RSA公司的ClearTrust等。
采用门户产品供应商自己的SSO产品,如:BEA的WLES,IBM 的Tivoli Access Manager,Sun 公司的identity Server,Oracle公司的OID等。
这些商业软件一般适用于客户对SSO的需求很高,并且企业内部采用COTS软件如:Domino,SAP,Sieble的系统比较多的情况下采用。并结合身份管理。统一认证等项目采用。采用这些软件一般都要对要集成的系统做些改造,如在要集成的系统上安装AGENT。现在一般只提供常见软件如:Domino,SAP,Sieble,常见应用服务器:weblogic,websphere等的AGENT。要先统一这些系统的认证。一般采用LDAP或数据库。然后才能实现SSO。比较麻烦。

另外,如果不想掏银子,也有OPEN SOURCE的SSO软件可选:主要有:http://www.josso.org/ https://opensso.dev.java.net/ http://www.sourceid.org 等。具体怎么样就不清楚了。
  如果项目对SSO的要求比较低,又不想对要被集成的系统做任何改动,可采用下面介绍的方式简单实现:下面我们通过一个例子来说明。假如一个门户项目要对下面的几个系统做SSO。

用户在这些系统中的用户名,密码各不相同,如:员工号为001的员工在这些系统中的用户名,密码分别如下:

用户 系统 用户名 密码
001 Portal系统 A 1234
001 邮件系统 B 2345
001 DOMINO系统 C AAAA
001 报销系统 D CCCC
001 工资系统 E BBBB

首先,建立员工在PORTAL系统中的用户名和其他系统中的用户名之间的对应关系
  首先,要建立员工在PORTAL系统中的用户名和其他系统中的用户名之间的对应关系并保存。可保存在表中或LDAP中或文件系统中。当然要考虑这些系统之间的数据同步问题。比较好的方式是找到用户在这些系统中的都存在的唯一信息(如员工号,MAIL地址,姓名等)。通过唯一信息实时到各个系统中去取认证所需要的信息。就不需要考虑数据同步问题。比较实用。可以建立类似下面的表:密码可采用加密保存。如果是采用BEA的Weblogic Portal,可采用UUP来保存这些信息。

( user varchar2(20), /*用户名*/ app_name varchar2(20), /*应用系统*/ architect varchar2(4), /*应用系统的架构BS或CS*/ app_company varchar2(50), /*用户所属分公司*/ app_department varchar2(50), /*用户所在的部门*/ app_user varchar2(15), /*在该系统中的用户名*/ app_passwd varchar2(15), /*在该系统中的密码*/ app_cookie varchar2(30), /*COOKIE名称*/ form_user varchar2(20), /*认证页面中FORM的用户名字段*/ form_passwd varchar2(20), /*认证页面中FORM的密码字段*/ app_special varchar2(20) /*其他*/ );通过IFRAME或超连接方式集成目标系统,并进行SSO
  通过IFRAME或超连接方式集成目标系统,并在URL中带上用户名和密码。如集成DOMINO可采用如下方式:

  

  或:
Href src=“http:// host1/names.nsf?Login&Username=admin&Password=pass&RedirectTo=/names.nsf”
以上采用的是在HTTP中直接传递明码,为提高安全性,可采用HTTPS来传递用户名和密码。另外采用这种方式被集成的系统必须支持FORM方式认证。J2EE应用,DOMINO等都支持FORM认证。

  这两种方式如果SSO成功,就自动进入目标系统的界面,如果实现会显示目标系统的登录界面。其效果图如下:


这种方式,必须维护对应关系表,如上面的sso_info。更好的方式是提供界面,让最终用户自己维护这种对应关系,可模仿Compoze portlets for lotus的做法,在用户第一次进入要与之做SSO的系统时,如DOMINO系统,显示一个界面,让用户自己输入他在该系统中的用户名/密码等信息。并保存到表中或LDAP等其他数据源中。以后用户要进入这些系统时,就直接从表中或其他数据源中取用户的用户名/密码等信息,帮助用户做认证。建议采用这种方式。如下图所示。如果用户改变了自己在DOMINO系统中的用户名,密码。从门户系统进入DOMINO系统时,认证会失败,就重新显示类似下面的界面。让用户重新输入他在DOMINO系统中新的用户名,密码并保存。


以上这种实现方式,一般需要浏览器支持COOKIE,所以要注意浏览器的配置,在开发阶段,为方便调试,可设置IE,让它显示COOKIE的名称。如下所示:


采用这种方式,对要集成的系统不需要做任何的改动。如果PORTAL系统中的用户在被集成的系统中的权限都一样,可采用建立一个通用用户的做法。也就是所有在PORTAL系统中的用户都采用这个通用用户进入目标系统。这种方式等于是采用页面集成方式做集成。比较方便使用。另外,有时候需要采用调用API,或配置Adapter等应用集成方式来集成其他系统,一般也是通过定义一个连接专用的用户。在API中或在配置Adapter的时候写死。如采用JAVA API方式集成DOMINO:

  lotus.domino.Session dominoSession = NotesFactory.createSession(dominoServer, “admin”, “password”);

CS结构实现方式
  经常有人问CS结构的应用如何实现SSO,本人的建议是对这种系统不要自己去实现SSO。很麻烦,其实输个用户名,密码没什么大不了的。如果要实现,一是采用商业软件。另外也可以采用以下方式:在PORTAL的PORTLET上建立超连接。并通过APPLET方式启动CS结构的应用系统的登录界面。然后通过如下的方式把用户名/密码传递过去。

  -不能做任何改动的客户端 - WIN消息(给登录窗口发送用户名,密码等登录所需要的信息),模拟键盘(java有模拟键盘输入的API)

  -可以做改动的客户端 - 参数传递,并让登录的EXE文件读取参数进行认证。

  因为要让APPLET执行本地的EXE文件,所以必须对IE中的JRE的安全进行设置。



其他:
  在采用以上方式实现了SSO后,要注意LOGOUT,可采用与LOGIN相同的方式。也可以通过被集成系统的超时设置来实现。

单点登录SSO技术资料收集
统一用户认证和单点登录解决方案: 计算机世界网上的文章,比较全面的介绍统一用户认证和单点登录解决方案

[转载]浅谈javascript函数劫持

创建时间:2007-12-02
文章属性:原创
文章提交:hkluoluo (luoluonet_at_hotmail.com)

by luoluo
on 2007-11-30
luoluonet_at_yahoo.cn
http://www.ph4nt0m.org

一、概述

javascript函数劫持,也就是老外提到的javascript hijacking技术。最早还是和剑心同学讨论问题时偶然看到的一段代码,大概这样写的:

window.alert = function(s) {};

觉得这种用法很巧妙新颖,和API Hook异曲同工,索性称之为javascript function hook,也就是函数劫持。通过替换js函数的实现来达到劫持这个函数调用的目的,一个完整的hook alert函数例子如下:

‘!–1.htm–>
‘script type=”text/javascript”>
‘!–
var _alert = alert;
window.alert = function(s) {
if (confirm(”是否要弹框框,内容是\”" + s + “\”?”)) {
_alert(s);
}
}
//–>
‘/script>
’html>
‘body>
‘input type=”button” onclick=”javascript: alert(’Hello World!’)” value=”test” />
’/body>
‘/html>

搞过API Hook的同学们看到这个代码一定会心的一笑,先保存原函数实现,然后替换为我们自己的函数实现,添加我们自己的处理逻辑后最终再调用原来的函数实现,这样这个alert函数就被我们劫持了。原理非常简单,下面举些典型的应用来看看我们能利用它来做些什么。

二、应用举例

1. 实现一个简易的javascript debugger,这里说是debugger比较标题党,其实只是有点类似于debugger的功能,主要利用js函数劫持来实现函数的break point,来看看个简单的例子:

‘script type=”text/javascript”>
‘!–
var _eval = eval;
eval = function(s) {
if (confirm(”eval被调用\n\n调用函数\n” + eval.caller + “\n\n调用参数\n” + s)) {
_eval(s);
}
}
//–>
‘/script>
'html>
'head>
'/head>
'body>
'script type=”text/javascript”>
‘!–
function test() {
var a = “alert(1)”;
eval(a);
}

function t() {
test();
}

t();
//–>
'/script>
'/body>
'/html>

通过js函数劫持中断函数执行,并显示参数和函数调用者代码,来看一个完整例子的效果:

>help
debug commands:

bp ‘function name> - set a breakpoint on a function, e.g. “bp window.alert”.
bl - list all breakpoints.
bc ‘breakpoint number> - remove a breakpoint by specified number, e.g. “bc 0″.
help - help information.

>bp window.alert
* breakpoint on function “window.alert” added successfully.

>bl
* 1 breakpoints:
0 - window.alert

>bc 0
* breakpoint on function “window.alert” deleted successfully.

这里演示设置断点,察看断点和删除断点,完整代码在本文附录[1]给出。

2. 设置陷阱实时捕捉跨站测试者,搞跨站的人总习惯用alert来确认是否存在跨站,如果你要监控是否有人在测试你的网站xss的话,可以在你要监控的页面里hook alert函数,记录alert调用情况:

'script type=”text/javascript”>
‘!–
function log(s) {
var img = new Image();
img.style.width = img.style.height = 0;
img.src = “http://yousite.com/log.php?caller=” + encodeURIComponent(s);
}

var _alert = alert;
window.alert = function(s) {
log(alert.caller);
_alert(s);
}
//–>
'/script>

当然,你这个函数要加到页面的最开始,而且还要比较隐蔽一些,赫赫,你甚至可以使alert不弹框或者弹个警告框,让测试者抓狂一把。

3. 实现DOM XSS自动化扫描,目前一般的XSS自动化扫描的方法是从http返回结果中搜索特征来确定是否存在漏洞,但是这种方法不适用于扫描DOM XSS,因为DOM XSS是由客户端脚本造成的,比如前段时间剑心发现的google的跨站(见附录[2])原理如下:

document.write(document.location.hash);

这样的跨站无法反映在http response里,所以传统扫描方法没法扫描出来。但是如果你从上个例子里受到启发的话,一定会想到设置陷阱的办法,DOM XSS最终导致alert被执行,所以我们hook alert函数设置陷阱,如果XSS成功则会去调用alert函数,触发我们的陷阱记录结果,这样就可以实现DOM XSS的自动化扫描,陷阱代码类似于上面。

4. 灵活的使用js劫持辅助你的页面代码分析工作,比如分析网页木马时,经常会有通过eval或者document.write来进行加密的情况,于是我们编写段hook eval和document.write的小工具,辅助解密:

'script type=”text/javascript”>
‘!–
var _eval = eval;
eval = window.execScript = window.document.write = window.document.writeln = function(s) {
document.getElementById(”output”).value = s;
}
//–>
'/script>
'html>
'body>
input:
'textarea id=”input” cols=”80″ rows=”10″>‘/textarea>
‘input type=”button” onclick=”javascript: _eval(document.getElementById(’input’).value);” value=”decode” />
‘br />
output:
‘textarea id=”output” cols=”80″ rows=”10″>‘/textarea>
'/body>
'/html>

在input框里输入加密的代码:

eval(unescape(”%61%6C%65%72%74%28%31%29%3B”));

在output框里输出解码后的代码:

alert(1);

当然你还能想到更多的灵活应用:)

三、反劫持

谈到劫持也就必然要谈谈反劫持的话题,假设你要写一个健壮的xss playload,就需要考虑反劫持,有两个问题要解决:

如何判断是否被劫持了?
如果发现被劫持了,如何反劫持?

1. 判断某个函数是否被劫持,这个好办,写个小程序对比一下一个函数被hook前后,有什么差别:

‘textarea id=”tb1″ cols=”80″ rows=”8″>‘/textarea>
‘script type=”text/javascript”>
‘!–
document.getElementById(”tb1″).value = eval + “\n”;
var _eval = eval;
eval = function(s) {
alert(s);
_eval(s);
}
document.getElementById(”tb1″).value += eval;
//–>
‘/script>

结果:

function eval() {
[native code]
}

function(s) {
alert(s);
_eval(s);
}

我们发现那些内置函数是[native code],而自定义的则是具体的函数定义,用这个特征就可以简单的检测函数是否被劫持:

function checkHook(proc) {
if (proc.toString().indexOf(”[native code]”) > 0) {
return false;
} else {
return true;
}
}

2. 如何反劫持,第一个想法就是恢复被劫持的函数,如果劫持的人把原函数保存在某个变量里那还好办,直接调用原函数就可以了,但是劫持者自己也没保存副本怎么办,只能自己创建个新的环境,然后用新环境里的干净的函数来恢复我们这里被hook了的,怎么创建新环境?整个新的iframe好了,里面就是个全新的环境。ok,动手吧:

function unHook(proc) {
var f = document.createElement(”iframe”);
f.style.border = “0″;
f.style.width = “0″;
f.style.height = “0″;
document.body.appendChild(f);

var d = f.contentWindow.document;
d.write(”‘script type=\”text/javascript\”>window.parent.escape = escape;’\/script>”);
d.close();
}

综合1、2节,整个测试代码如下:

‘!–antihook.htm–>
‘script type=”text/javascript”>
‘!–
escape = function(s) {
return s;
}
//–>
‘/script>
‘html>
‘body>
‘input type=”button” onclick=”javascript: test();” value=”test” />
‘script type=”text/javascript”>
‘!–
function test() {
alert(escape(”s y”));

if (checkHook(escape)) {
unHook(escape);
}

alert(escape(”s y”));
}

function checkHook(proc) {
if (proc.toString().indexOf(”[native code]”) > 0) {
return false;
} else {
return true;
}
}

function unHook(proc) {
var f = document.createElement(”iframe”);
f.style.border = “0″;
f.style.width = “0″;
f.style.height = “0″;
document.body.appendChild(f);

var d = f.contentWindow.document;
d.write(”‘script type=\”text/javascript\”>window.parent.escape = escape;’\/script>”);
d.close();
}
//–>
‘/script>
‘/body>
‘/html>

3. 不是上面两个问题都解决了么,为什么要有第3节?因为那不是个最好的解决办法,既然我们可以创建全新的iframe,何不把代码直接放到全新iframe里执行呢,这样做的话绿色环保,既不用考虑当前context里的hook问题,也不用改动当前context,不会影响本身的程序执行。给出两个比较通用点的函数:

function createIframe(w) {
var d = w.document;
var newIframe = d.createElement(”iframe”);
newIframe.style.width = 0;
newIframe.style.height = 0;
d.body.appendChild(newIframe);
newIframe.contentWindow.document.write(”‘html>‘body>‘/body>‘/html>”);
return newIframe;
}

function injectScriptIntoIframe(f, proc) {
var d = f.contentWindow.document;
var s = “‘script>\n(” + proc.toString() + “)();\n’/script>”;
d.write(s);
}

把你的payload封装进一个函数,然后调用这两个方法来在iframe里执行:

function payload() {
// your code goes here
}

var f = createIframe(top);
injectScriptIntoIframe(f, payload);

四、最后

由于国内很少有见文章提及这个东西,所以才草成这篇,希望能够抛砖引玉。由于本人水平有限,难免有错误或者疏漏之处请谅解,没有说清楚的地方,欢迎和我交流。

还有就是一些不得不感谢的人,感谢剑心一直以来毫无保留的交流,感谢黑锅多次鼓励我把自己的心得体会写成文字,感谢幻影所有的朋友们、B.C.T的朋友们以及群里那帮经常一起扯淡的朋友们。

广告一下,没法幻影blog的朋友,可以添加hosts来突破:
72.14.219.190 pstgroup.blogspot.com

五、附录

[1] 简易的javascript inline debugger代码

‘!–test.htm–>
‘!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
‘html xmlns=”http://www.w3.org/1999/xhtml”>
‘head>‘title>Javascript Inline Debugger’/title>‘/head>
‘body>
‘script language=”javascript” type=”text/javascript” src=”js_inline_debugger.js” mce_src=”js_inline_debugger.js”>‘/script>
‘input type=”button” value=”hahaha” onclick=”javascript: alert(this.value);” style=”margin-left: 300px;” />
‘/body>
‘/html>

/*
FileName: js_inline_debugger.js
Author: luoluo
Contact: luoluonet_at_yahoo.cn
Date: 2007-6-27
Version: 0.1
Usage:
‘!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
‘html xmlns=”http://www.w3.org/1999/xhtml”>
‘head>
‘/head>
‘body>
‘script language=”javascript” type=”text/javascript” src=”js_inline_debugger.js” mce_src=”js_inline_debugger.js”>‘/script>
‘/body>
‘/html>
Instruction:
It is a simple javascript inline debugger. You must add xhtml1-transitional dtd to your
html document if you wanna to use the script.
*/

//————————————————————————–//
// 公用的函数
//————————————————————————–//
// 判断是否是IE
function isIE() {
return document.all && window.external;
}

// 去除字符串两边的空格
String.prototype.trim = function() {
var re = /(^\s*)|(\s*)$/g;
return this.replace(re, “”);
}

// 删除数组中某个元素
Array.prototype.remove = function(i) {
var o = this[i];
for (var j = i; j ‘ this.length - 1; j ++) {
this[j] = this[j + 1];
}
– this.length;
return o;
}

// 判断一个数组中是否存在相同的元素
Array.prototype.search = function(o) {
for (var i = 0; i ‘ this.length; i ++) {
if (this[i] == o) {
return i;
}
}

return -1;
}

// html编码
function htmlEncode(s) {
s = s.replace(/&/g, “&”);
s = s.replace(/’/g, “<”);
s = s.replace(/>/g, “>”);
s = s.replace(/\”/g, “"”);
s = s.replace(/\’/g, “"”);

return s;
}

// js编码
function jsEncode(s) {
s = s.replace(/\\/g, “\\\\”);
s = s.replace(/\n/g, “\\n”);
s = s.replace(/\”/g, “\\\”");
s = s.replace(/\’/g, “\\\’”);
return s;
}

//————————————————————–//
// 命令行窗口工具
//————————————————————–//
function Console(parentNode, processInputProc) {
// 窗口
var panel = document.createElement(”div”);
panel.style.width = “250px”;
panel.style.height = “250px”;
panel.style.borderColor = “#666666″;
panel.style.borderWidth = “1px”;
panel.style.backgroundColor = “#ffffff”;
panel.style.borderStyle = “solid”;
panel.style.position = “absolute”;
panel.style.zIndex = 100;
parentNode.appendChild(panel);

// 标题栏
var title = document.createElement(”div”);
title.style.width = “250px”;
title.style.height = “15px”;
title.style.backgroundColor = “#dddddd”;
title.style.fontSize = “12px”;
title.style.fontFamily = “verdana,tahoma”;
panel.appendChild(title);

// 标题栏拖动窗口功能
var isDragging = false;
var startPos = new Position(panel.offsetLeft, panel.offsetTop);
var startMousePos = new Position(0, 0);

title.onmouseover = function(evt) {
this.style.cursor = “pointer”;
}

title.onmousedown = function(evt) {
if (isDragging == true) {
return;
}

var event = evt || window.event;
startMousePos.x = event.clientX;
startMousePos.y = event.clientY;
isDragging = true;
}

title.onmousemove = function(evt) {
if (isDragging == false) {
return;
}

activateWindow();
var event = evt || window.event;
panel.style.left = (event.clientX - startMousePos.x) + startPos.x + “px”;
panel.style.top = (event.clientY - startMousePos.y) + startPos.y + “px”;
}

title.onmouseup = function(evt) {
if (isDragging == false) {
return;
}

var event = evt || window.event;
startPos.x = (event.clientX - startMousePos.x) + startPos.x;
panel.style.left = startPos.x + “px”;
startPos.y = (event.clientY - startMousePos.y) + startPos.y;
panel.style.top = startPos.y + “px”;
isDragging = false;
}

// 关闭窗口功能
var closeButton = document.createElement(”div”);
closeButton.style.width = “13px”;
closeButton.style.height = “13px”;
closeButton.style.backgroundColor = “#ffffff”;
closeButton.style.styleFloat = closeButton.style.cssFloat = “left”;
closeButton.style.fontSize = “12px”;
closeButton.style.borderWidth = “1px”;
closeButton.style.borderColor = “#999999″;
closeButton.style.borderStyle = “solid”;
closeButton.style.paddingButton = “2px”;
closeButton.innerHTML = “‘div style=\”margin-top: -2px;margin-left: 3px;\”>x’/div>”;
title.appendChild(closeButton);

var isVisible = true;

// 窗口关闭
closeButton.onclick = function(evt) {
isVisible = false;
panel.style.display = “none”;
}

// 标题栏文字
var titleLabel = document.createElement(”div”);
titleLabel.style.height = “14px”;
titleLabel.style.width = “200px”;
titleLabel.style.fontSize = “11px”;
titleLabel.style.color = “#ffffff”;
titleLabel.style.styleFloat = titleLabel.style.cssFloat = “left”;
titleLabel.style.fontFamily = “verdana,tahoma”;
titleLabel.innerHTML = “Javascript Inline Debugger”;
//titleLabel.style.marginTop = isIE() ? -14 : “-14px”;
titleLabel.style.marginLeft = isIE() ? 18 : “18px”;
title.appendChild(titleLabel);

// 中间的显示部分
var showPanel = document.createElement(”div”);
showPanel.style.width = “250px”;
showPanel.style.height = isIE() ? “221px” : “219px”;
showPanel.style.fontSize = “11px”;
showPanel.style.padding = “0″;
showPanel.style.margin = “0″;
showPanel.style.overflow = “auto”;
showPanel.style.marginTop = isIE() ? -1 : “0″;
panel.appendChild(showPanel);

// 命令输入框
var cmdArea = document.createElement(”div”);
panel.appendChild(cmdArea);

var cmdTextbox = document.createElement(”input”);
cmdTextbox.type = “text”;
cmdTextbox.style.width = “250px”;
cmdTextbox.style.height = “12px”;
cmdTextbox.style.fontSize = “12px”;
cmdTextbox.style.padding = “0″;
cmdTextbox.style.margin = “0″;
cmdTextbox.style.marginTop = isIE() ? -2 : “0″;
cmdTextbox.style.borderWidth = “0″;
cmdTextbox.style.borderTopWidth = “1px”;
cmdTextbox.style.paddingTop = “2px”;
cmdArea.appendChild(cmdTextbox);

// 窗口激活或者不激活
var isActive = false;

// 激活窗口
var activateWindow = function() {
if (! isVisible) {
return;
}

if (isActive) {
return;
}

cmdTextbox.focus();
title.style.backgroundColor = “#015DF4″;
isActive = true;
}

panel.onclick = activateWindow;

// 不激活窗口
var deactivateWindow = function() {
if (! isVisible) {
return;
}

if (! isActive) {
return;
}

title.style.backgroundColor = “#cccccc”;
isActive = false;
}

cmdTextbox.onfocus = activateWindow;
cmdTextbox.onblur = deactivateWindow;

// 输出函数
var dbgPrint = function(s) {
showPanel.innerHTML += htmlEncode(s).replace(/\n|(\r\n)/g, “‘br />”);

if (parseInt(showPanel.scrollHeight) > parseInt(showPanel.style.height)) {
showPanel.scrollTop += parseInt(showPanel.scrollHeight) - parseInt(showPanel.style.height);
}
}

// 调用输入处理回调函数
cmdTextbox.onkeydown = function(evt) {
var event = evt || window.event;

if (event.keyCode == 0×0d) {
processInputProc(this.value, dbgPrint);
this.value = “”;
}
}
}

// 坐标类
function Position(x, y) {
this.x = x;
this.y = y;
}

//————————————————————————–//
// 内联调试器类
//————————————————————————–//
function InlineDebugger() {
var bpList = new Array();
var id_eval;

// 设置断点
var bp = function(funcName) {
// 检查eval是否被hook
if ((new String(eval)).indexOf(”[native code]”) ‘ 0) {
return “error: eval function was hooked by other codes in the front.\n”;
}

// 保存未被hooked的eval
id_eval = eval;

var re = /^[a-zA-Z0-9_\.]+$/i;
if (! re.test(funcName)) {
return “error: bad argument of command bp \”" + funcName + “\”.\n”;
}

try {
id_eval(”if (typeof(” + funcName + “) == \”object\” || typeof(” + funcName + “) == \”function\”) {var obj = ” + funcName + “;}”);
} catch (e) {
return “error: ” + e + “, invalid function name \”" + funcName + “\”.\n”;
}

if (obj == undefined) {
return “error: the argument of command bp \”" + funcName + “\” is not a function object.\n”;
}

if ((new String(obj)).indexOf(”function”) ‘ 0) {
return “error: the argument of command bp \”" + funcName + “\” is a property, a function is required.\n”;
}

if (bpList.search(funcName) >= 0) {
return “error: there is a breakpoint on function \”" + funcName + “\”\n”;
}

try {
var sc = “window.” + funcName.replace(/\./g, “_”) + “_bak = ” + funcName + “;\n” +
funcName + ” = ” +
“function() {\n” +
“ var args = \”\”;\n” +
“ for (var i = 0; i ‘ arguments.length; i ++) {\n” +
“ args += arguments[i] + (i == (arguments.length - 1) ? \”\” : \”,\”);\n” +
“ }\n” +
“ if (confirm(\”function \\\”" + funcName + “\\\” was called, execute it?\\n\\narguments:\\n\” + args + \”\\n\\ncaller:\\n\” + ” + funcName + “.caller)) {\n” +
“ id_eval(\”" + funcName.replace(/\./g, “_”) + “_bak(\\\”\” + jsEncode(args) + \”\\\”)\”);\n” +
“ }\n” +
“};” +
“\n”;
id_eval(sc);
bpList.push(funcName);
return “* breakpoint on function \”" + funcName + “\” added successfully.\n”;
} catch (e) {
return “unkown error: ” + e + “.\n”;
}
}

// 枚举所有的断点
var bl = function() {
if (bpList.length == 0) {
return “* no breakpoint.\n”;
}

var bps = “* ” + bpList.length + ” breakpoints: \n”;

for (var i = 0; i ‘ bpList.length; i ++) {
bps += i + ” - ” + bpList[i] + “\n”;
}

return bps;
}

// 清除某个断点
var bc = function(n) {
try {
n = parseInt(n);
} catch (e) {
return “error: bc command requires a numeric argument.\n”;
}

if (bpList.length == 0) {
return “error: no breakpoint.\n”;
}

var funcName = bpList.remove(n);

try {
eval(funcName + ” = window.” + funcName.replace(/\./g, “_”) + “_bak;”);
return “* breakpoint on function \”" + funcName + “\” deleted successfully.\n”;
} catch (e) {
return “error: ” + e + “.\n”;
}
}

// 帮助
var help = function() {
var s = “debug commands:\n\n” +
”bp ‘function name> - set a breakpoint on a function, e.g. \”bp window.alert\”.\n” +
”bl - list all breakpoints.\n” +
”bc ‘breakpoint number> - remove a breakpoint by specified number, e.g. \”bc 0\”.\n” +
”help - help information.\n”
”\n”;
return s;
}

// 处理命令
this.exeCmd = function(cmd) {
cmd = cmd.trim();
var cmdParts = cmd.split(/\s+/g);
var cmdName;
var cmdArg;

if (cmdParts.length == 1) {
cmdName = cmd;
} else {
cmdName = cmdParts[0];
cmdArg = cmdParts[1];
}

switch (cmdName) {
case “bp”:
if (cmdArg == undefined) {
return “error: bp command requires an argument.\n”;
} else {
return bp(cmdArg);
}
break;

case “bl”:
return bl();
break;

case “bc”:
if (cmdArg == undefined) {
return “error: bc command requires an argument \”number of breakpoint\”.\n”;
} else {
return bc(cmdArg);
}
break;

case “help”:
return help();
break;

default: return “error: command \”" + cmdName + “\” not found, you can get information by \”help\” command.\n”;
break;
}
}
}

//—————————————————————————–//
// 主过程
//—————————————————————————–//
/*try {
debugger;
} catch (e) {}*/
var id = new InlineDebugger();
var console = new Console(document.body, function(s, printProc){printProc(id.exeCmd(s));});

[2] http://www.xfocus.net/articles/200711/957.html

[转载]安全登录的跨平台解决方案V1.1

创建时间:2007-11-26
文章属性:原创
文章提交:ph4_yunshu (wustyunshu_at_hotmail.com)

Team: http://www.ph4nt0m.org
Author: 云舒(http://www.icylife.net/)
Date: 2007-11-23

最近在考虑一个安全可靠的,容易部署的方案来解决登陆的安全问题,因为这个问题随着网上银行,网络游戏等的兴起而越来越重要。之所以想跨平台,是因为最近很多的linux用户在网上讨论linux,firefox这些使用网银等时候遇到的麻烦。我恰好看了他们的很多讨论,除了口水之外没有别的东西,只废话而不做事的废柴太多了。因此,我就有了这么一篇文章,介绍一下怎么实现安全的登陆。题外话一句,我对web安全的各种技巧,其实没有太大的兴趣,虽然闲得没事也会偶尔玩玩,只是和我玩CS差不多而已。

一. 登陆面临的安全攻击

1. 键盘记录

键盘记录应该是目前最流行的盗取密码的方法了,攻击者在系统中使用全局键盘钩子,记录用户的按键操作,并根据用户窗口的标题来确定是不是自己需要盗取的东西。

2. 系统消息

用户在浏览器中敲完了密码之后,攻击者的程序给浏览器发送消息,要求取得密码输入框的值,随后保存并记录下来。在windows2000以后的系统中,做了一点改进,但是创建了远程线程之后就依旧可以了。

3. 传输捕获

攻击者在网络中劫持或者嗅探传输数据,直接获取明文密码。虽然现在https使用较多,但是https速度较慢,影响服务器速度,而且也存在比较严重的一些安全缺陷,并不是一种非常可靠的办法。关于https的劫持,可以看我的一个相关文章《如何进行https中间人攻击》,地址在http://www.icylife.net/yunshu/show.php?id=468

二. 当前的解决办法以及缺陷

1. 当前国内解决方案

目前国内最常见的解决办法是ActiveX控件加https的方式。使用ActiveX控件来保护输入不被键盘记录和伪造的系统消息获取,https来保证传输的隐秘性和完整性。招行,民生银行的登陆大致都是这样的模式,因此国内某些firefox用户比较不满。我曾经在一个帖子中说过键盘记录的问题,被说成是银行的枪手,这里就不说了。

2. ActiveX的缺陷

https的缺陷就不说了,很容易就可以伪造证书劫持传输,虽然会提示证书警告,但是有多少用户会在意了?而且国内不少银行自己的证书本来就会有安全警告提示。ActiveX的缺陷也很明显,首先只支持windows系统和IE浏览器;其次本身容易出现安全问题,造成溢出等攻击;最后,对于更底层的挂接I/O驱动的攻击比较为难,当然ActiveX也可以调用自己的驱动,但是成本太高了。

三. 跨平台的解决方案

1. 解决方案

我这里的方案是一个思路,不局限于语言。如果不想跨平台你可以使用ActiveX实现,如果需要跨平台可以使用java applet实现,甚至还可以使用flash来实现。利用的方法主要是随机键位的软键盘以及RSA非对成加密。RSA也是一个很有意思的东西,可以看看《RSA非对称加密的一些非常规应用》,地址为http://www.icylife.net/yunshu/show.php?id=471

目前常见的登陆都是html等语言作的,然后调用ActiveX控件或者java applet来输入密码。其实,最好的解决方案是直接使用java applet作一个form,用来接受用户名和密码,做出相应的处理后再登陆。

首先,在服务器端生成一对2048位的RSA密钥,私钥保存好,公钥则发布到网上,让客户端可以通过类似http://www.test.com/login/publickey.txt的域名来进行访问。

其次,在客户端用java applet作一个form来等待用户输入。用户名常规方法输入,而密码框得到焦点时,弹出虚拟键盘。键盘每个键的位置随机出现,防止别人记录。而在java applet中,有一个变量real_pass,在用户点了一个键,比如说是点了a的时候,real_pass += ‘a’,同时在密码输入框中随机输入一个字符,直到用户密码输入完成。这个时候,真实的密码记录在real_pass变量中,而密码输入框中是一个伪造的密码。

再次,用户点击提交按钮的时候,客户端从服务端获取RSA公钥,对real_pass进行RSA非对称加密,然后提交到服务端等待认证。

2. 优势

最明显的优势是使用了java applet来进行客户端操作,而java是可以跨平台,跨浏览器的。因此firefox用户,linux用户都可以成功的使用。其次,java是一种安全的语言,不会存在ActiveX自身容易出现的缓冲区溢出等安全问题。

3. 缺陷

使用java applet的缺陷主要有三点:第一,jre过于庞大,而且还没有普及到所有的用户当中;第二,jre也出现过一些溢出类的安全漏洞,因为jre本身是使用c写的;第三,java applet的运行速度不如ActiveX快。当然,可以使用ActiveX技术实现同样的思路,只是那样就不能跨平台了,违背了设计的初衷。

四. 总结

这个只是安全登陆的一个思路,和具体的语言以及平台都没有关系,可以用java applet实现,可以用ActiveX实现,,也可以用flash实现,甚至还可以使用js来实现,只要你能用js画出那个键位随机的键盘,后面的真假密码,以及RSA加密等js实现起来就不难了。其实js也是个不错的选择,而且还没有客户端环境需求。关于钓鱼的内容,我们team的刺写过一些文章,我就不多说了。本文可以随意转载,但是请保留出处和版权,并且不得用于商业用途,谢谢。

五. 补充

下午写完这个方案后发布到了我们team的blog和邮件列表,算是1.0版本。后来和刺,螺螺做了一番讨论,还谈到了上面说过的《RSA非对称加密的一些非常规应用》中关于代替https的部分。这里确实存在一个安全缺陷,因此作了这个补充,算是这个方案的1.1版。

主要是因为用JS来实现RSA加密代替HTTPS,也存在中间人攻击的问题。明文传输的JS中保存了public key,攻击者可以作中间人,在客户端请求服务端的时候,替换掉这个public key,从而得到传输的密码。

在现在提出的这个解决方案的1.0版中,我提出”在服务器端生成一对2048位的RSA密钥,私钥保存好,公钥则发布到网上,让客户端可以通过类似http://www.test.com/login/publickey.txt的域名来进行访问。“这样其实是不够安全的。这么作的原因是可以很方便的修改public key而不用重新编译java applet或者flash程序,但是带来的后果是如果有人在传输这个public key的时候劫持了会话,可能会自己生成一对RSA密钥,将其中的公钥作为真实服务器的公钥传递给登陆用户,最终可获取传输的明文密码。

解决这个问题并不困难,有两个办法:第一,可以使用https的方式来传输这个public key,像https://www.test.com/login/publickey.txt这样来访问。在出现证书报警的时候提示用户不要进行下一步操作,终止整个登陆过程。这样public key依旧可以方便的修改而不用重新编译。如果要更高的安全性,可以整个过程都使用https来进行。第二,如果因为HTTPS会给服务器带来较大运算压力而又不想买硬件加密卡,也可以将public key直接写在java applet或者flash中。这样不用每次去抓取public key,只是定期更换public key会比较麻烦,需要重新编译java applet或者flash程序。这样需要注意的是,攻击者可能直接arp spoof然后发一个假的applet小程序过来。总的来说,全过程的https是比较好的,即使被劫持,也能够出现证书警告。

最后,说一下屏幕截取。刺提起过这个问题,我在构思这个解决方案的时候,想到过这种攻击。不过屏幕截取来查看用户的鼠标点击,还是比较困难的,难以截取准确的时间段。防御的方法,或许就只能写驱动了。

关于刺的文章,可以在我们的blog查看,url为http://pstgroup.blogspot.com/2007/11/tipsqqrsa.html(需要代理穿越GFW)。腾讯的注册那里,使用的js加密代替https的方法,可以看我以前写的文章。他们会不会买硬件加密卡?呵呵。

CSV文件格式简介

CSV即Comma Separate Values的缩写,现在常见到这种格式的文件,例如Google adsense的自定义报表就有这种格式。

绝大多数知道CSV的人都认为这个格式个归属于Excel家族的东西,至少我们不确定的时候可以提出一个佐证:大部分的时候我们都使用excel来操作CSV格式。是的,我的确要再这里说一个”但是”——但是我真不能确定这个格式是否就是微软定义的,可以确定的是,这个格式是为了异构下传递数据的时候使用的。

windows平台下一般将CSV文件定义为xml后缀,用文本编辑器就可以打开。

在项目中进行报表设计时,考虑到方便灵活及让客户可自行修改格式且保存数据,我想将数据输出到CSV,而不是传统意义上的Excel格式,这个时候需要我对CSV的格式进行了解,CSV的主站在

http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm

具体多少人知道确切的CSV格式?如果在一个字段中存在回车换行如何处理?字段的开始和结束的双引号有什么用?我想没有使用的时候,没人关心这个问题,因为大部分的时候我们使用excel打开CSV文件,然后再使用Excel转换CSV文件,没人关心这个CSV文件里面具体包含了什么东西。

CSV格式的具体规范如下

每条记录为一行
除非记录的字段中包含一个回车/换行符号,这样会导致这条记录保存为多行。换行的字段必须使用双引号括起来。
字段之间使用逗号进行分割
字段的前置空格和后置空格都被自动忽略。可以使用双引号括起来以保持这些空格。
字段中如果包含逗号的值,那么这个字段必须使用双引号括起来。
字段中包含双引号的时候使用两个双引号进行转义。
第一行的数据可以是列的头信息
这个世界想当然的东西太多了,呵呵,那么JSON的规范几个人又看过呢?简单,的确简单,但是并不意味着简单就没有内涵。

CSV有个升级版的兄弟,CTX,也在同一个站点上,感兴趣的自己去看看把。

The CSV (”Comma Separated Value”) file format is often used to exchange data between disparate applications. The file format, as it is used in Microsoft Excel, has become a pseudo standard throughout the industry, even among non-Microsoft platforms.

As is the case with most exchange formats since XML, CSV files have become somewhat of a legacy format. New applications that wish to include an export format will generally use XML today (though there may be exceptions). In legacy systems though (pre-XML), CSV files had indeed become a de facto industry standard. Just as there are still billions of lines of CoBOL code in use today that need to be maintained, support for a legacy standard such as CSV is likely to be required long after it has stopped being implemented in new designs.

把Flash动画轻松转成GIF图片[经典]

在FLASH里,可以用发布,或导出的形式来作,但是,有很多的限制和错误~

在这,给朋友介绍一种小软件,可以把Flash动画轻松转成GIF图片~请朋友们试一下~

把Flash动画轻松转成GIF图片

Magic Swf2Gif不但可以快速高效地将SWF格式的Flash动画转换为GIF动画图片,而且在转换之后还能完整地保持原Flash动画中的动画帧数和分辨率。

第一步:导入SWF 动画文件


点击软件界面中的“Add Files”按钮,在弹出的对话框中选择需要转换的Flash动画文件,在添加文件时可以一次选择多个Flash动画文件批量导入,当然也可以点击“Add DIR”按钮将某个目录下的SWF文件全部导入。

在导入文件时有一个特色,那就是除了可以添加文件和目录之外,还可以在点击“IE Cache”按钮后直接将IE缓存中的SWF文件添加进列表中。

第二步:截取要转换的动画片段

在SWF文件列表中,选择一个Flash动画。这时我们可以看到原来的“文件信息”按钮已经显示为“W:500,H:400,T:2496,R: 25”字样,如图所示,这是什么意思呢?点一下按钮你就知道了。原来,这表示该Flash动画的宽度为500像素,高度为400像素,总帧数为2496 帧,帧频为25帧/秒。

现在需要设置欲转换动画的起止点,以去掉自己不想要的开始或结尾片段。在右边的播放框中,拖动下方滑块或者用播放按钮将Flash动画停留在转换部分的起始处(如第1000帧),随后点击动画下方的“[”按钮在此处做上起始标记。

同样,拖动滑块或者用播放按钮将Flash动画停留在转换部分的结尾处(如第1500帧),并点击“]”按钮在此处做上结束标记。

第三步:GIF动画的输出设置

点击“Options”按钮,先在“Resize”选项卡中选择生成GIF动画的图片尺寸大小,既可以选择预设的大小(默认为原始大小),也可以自定义大小,然后在“GIF Frame Rate”选项卡中设置GIF动画的帧率。

注意:当减少帧频的时候,软件将不会更改电影的播放速度,只是放弃一些帧以减小GIF文件的体积。最后切换到“目录”选项卡设置好转换后的GIF文件的输出目录。

当一切准备完毕,我们就可以点击“Convert GIF”按钮,很快即可将所选定的Flash动画片段转换为GIF格式的动画文件。

小提示:如果我们只需要抓取该Flash动画中的某一个帧,可以点击“0”按钮将当前选定的帧捕捉为一个位图文件。

软件下载

注册码如下:

4D616769-63201992-92D9C0E5-95902A9C-3F76F46E-DCE63F79-1F9F1D1E-DC6226D0-4FA72789-C3B92E34-EC454F44-9E5F224C-F30D83F5-69DA507B-8174EF00

java中报表打印通过生成CSV格式文件来解决

最近的项目需要对数据进行查询并声称报表打印,也就是把数据库中的字段遍历后输出;

撇开使用其他控件外直接来生成统计报表,一般常用的方法都是直接输出为一个新的htm页面.这个方法的好处是所见即所的。但是缺点是调整格式特别麻烦.需要在dw或frontpage中调后格式后,遍历玩数据后往中写数据。

但是考虑到维护性,而且方便性,可让用户自己调整打印格式,并可下载保存统计报表,最方便的就是生成CSV文件,让用户下载保存,自己排版并打印;代码如下:

//生成日志CSV文件
if (cmd.equals(”CreatelogCsv”)){
String type=request.getParameter(”type”);
response.setContentType(”text/csv”);
response.setHeader(”Cache-Control”, “no-cache”);
response.setCharacterEncoding(”GB2312″);
response.addHeader(”Content-Disposition”, “attachment; filename=\”"+URLDecoder.decode(”report.csv”,”GB2312″)+ “\”");
response.getWriter().write(”操作人员,操作类型,操作内容,访问IP,操作时间\r\n”);
List list =null;

if (type.equals(”syslog2″)){
//系统管理员全部日志
list =logdao.querysyslogbydate(”1000000000″,”",0,0);
}

if (list!=null && list.size()>0){
Iterator it =list.iterator();
while (it.hasNext()){
LogId logidinfo = (LogId) it.next();
response.getWriter().write(URLDecoder.decode(logidinfo.getLogUser(),”GB2312″)+”,”
+URLDecoder.decode(logidinfo.getLogType(),”GB2312″)+”,”
+URLDecoder.decode(logidinfo.getLogContent(),”GB2312″)+”,”
+URLDecoder.decode(logidinfo.getLogIp(),”GB2312″)+”,”
+URLDecoder.decode(logidinfo.getLogData(),”GB2312″)+”\r\n”);
}
}

}

讨论Hibernate通过criteria对时间的处理

环境 :oracle9i,Tomcat5.0,winxp测试通过 ;

Hibernate中对时间的处理直接使用hql肯定是最方便的,但是criteria示例查询又是不非常不错的选择,在criteria中如何对时间进行查询。

Criteria criteria = getSession().createCriteria(GasthausInfo.class);

//按照时间来查询
if (hostel.getRegisterdate()!=null){
//按照注册时间来查询
residedatestr = PubMethod.parse(hostel.getRegisterdate(),”yyyy-MM-dd”);
criteria.add(Expression.between(”registerdate”,
PubMethod.parse((residedatestr+” 00:00:01″),”yyyy-MM-dd HH:mm:ss”),
PubMethod.parse((residedatestr+” 23:59:59″),”yyyy-MM-dd HH:mm:ss”)));
}

criteria.add(Example.create(hostel).excludeProperty(”registerdate”));//屏蔽掉示例查询中对registerdate字段。然后通过Expression.between()来进行查询,

数据库中registerdate字段为date 或varchar都支持。。。

Hibernate使用Criteria对复合主键的处理、查询

Hibernate复合主键,如果把一个表分成俩部分,一部分是,一部分是时,一般的处理相信大家都知道.但如果通过criteria对复合主键进行处理,那么会出现个问题.criteria.add(Expression.eq(”字段映射名”,值)),这个地方的字段映射名应该怎么写?网上有资料说通过别名来操作到是个不错的方法,但下面是讨论通过直接把表的字段都影射成为复合主健,只映射为一个类进行直接处理;
ex:
class name=”com.hostel.db.entity.log.LogId” table=”LOG” schema=”TEGUAN”>
'composite-id>
'key-property name=”logId” type=”java.lang.String”>

'/key-property>
'key-property name=”logData” type=”java.lang.String”>

'/key-property>
'key-property name=”logType” type=”java.lang.String”>

'/key-property>
'key-property name=”logContent” type=”java.lang.String”>

'/key-property>
'key-property name=”logUser” type=”java.lang.String”>

'/key-property>
'key-property name=”logIp” type=”java.lang.String”>

'/key-property>
"/coposite-id>
'/class

通过这样,只需要操作一个类来完成。这样对于使用criteria来查询查询是个不错的选择;

使用Criteria查询不能通过主健进行查询(兴许是笔者自己太弱了),这时就首先需要把主健字段屏蔽掉。如下代码:

criteria.add(Example.create(hostel)
.enableLike(MatchMode.ANYWHERE) //模糊查询
.excludeProperty(”registerdate”) //取消按照注册时间、审核时间、旅馆ID 进行示例查询
.excludeProperty(”auditdate”)
.excludeProperty(”gasthausId”));

这样就可以屏蔽掉按某些字段查询,然后在通过criteria.add(Expression.eq(”",”"))来进行查询了;

注:如果查询字段没有通过.excludeProperty屏蔽示例查询,那么 .enableLike(MatchMode.ANYWHERE) //模糊查询 将会自动进行模糊查询,而不用使用Expression.like(”字段名”,”%”+值+”%”);

[转载]通过过LDAP实现IIS和domino之间的单点登陆

转载自:http://bbs.chinalotus.com/viewthread.php?tid=34332

原理:
IIS中的页面通过Domino的LDAP服务验证用户名密码是否正确,
通过LDAP验证后,生成cookie,然后登陆domino
环境:
IIS 6.0,ASP.NET 2.0,Domino 7.0.2
条件:
IIS站点使用Forms验证方式,并禁止匿名用户访问;
Domino的LDAP服务配置:当前服务器文档->端口->internet端口->目录->验证选项->匿名=否。
局限:
登陆页面必须放在IIS的站点中,登录后只能进入domino中的页面,
此时需要domino中的页面提供进入IIS站点的连接;
如果是未验证的请求访问IIS站点中的页面,在登陆后不能重定向到该页面,只能进入domino中的首页。

附件里是asp.net2.0 webset的源代码

IISDominoSSO.rar (7.71 KB)

用的时候注意修改webconfig

[转]仿照hibernate 生成无重复主键

用过hibernate的人一定都知道uuid.hex主键生成方式,非常爽。

由于其生成的ID的特殊方式,很受大家喜爱。

我把hibernate这段代码搬了出来。整理了一下。方便大家用在其它地方,如附件名生成等。

代码如下:

/*
* Last Modify: 2005年1月13日 1:57:4
*/
package com.schoolwx.core.id;

import java.net.InetAddress;
import java.util.Properties;

/**
* Filename: UUIDHex.java

* Ttitle: HEX类型主键生成器生成类

* Description:

* Copyright: Copyright (c) 2001-2004 schoolwxr,Inc.All Rights Reserved.

* Company: bluestar

* Author: dkmilk

* Telephone: 86-28-85239210

* Date: 2004-9-14

* Time: 12:50:43

* Version: 2.0.0

*/
public class UUIDHex {

private static final int IP;

private String sep = “”;

static {
int ipadd;
try {
ipadd = toInt(InetAddress.getLocalHost().getAddress());
} catch (Exception e) {
ipadd = 0;
}
IP = ipadd;
}

private static short counter = (short) 0;
private static final int JVM = (int) (System.currentTimeMillis() >>> 8);
/**
* Unique across JVMs on this machine (unless they load this class
* in the same quater second - very unlikely)
*/
private int getJVM() {
return JVM;
}

/**
* Unique in a millisecond for this JVM instance (unless there
* are > Short.MAX_VALUE instances created in a millisecond)
*/
private short getCount() {
synchronized (UUIDHex.class) {
if (counter < 0) counter = 0;
return counter++;
}
}

/**
* Unique in a local network
*/
private int getIP() {
return IP;
}

/**
* Unique down to millisecond
*/
private short getHiTime() {
return (short) (System.currentTimeMillis() >>> 32);
}

private int getLoTime() {
return (int) System.currentTimeMillis();
}

private static int toInt(byte[] bytes) {
int result = 0;
for (int i = 0; i < 4; i++) {
result = (result << - Byte.MIN_VALUE + (int) bytes[i];
}
return result;
}

public void configure(Properties params) {
sep = params.getProperty(”seperator”);
}
protected String format(int intval) {
String formatted = Integer.toHexString(intval);
StringBuffer buf = new StringBuffer(”00000000″);
buf.replace(8 - formatted.length(), 8, formatted);
return buf.toString();
}

protected String format(short shortval) {
String formatted = Integer.toHexString(shortval);
StringBuffer buf = new StringBuffer(”0000″);
buf.replace(4 - formatted.length(), 4, formatted);
return buf.toString();
}

public String generate() {
return new StringBuffer(36)
.append(format(getIP())).append(sep)
.append(format(getJVM())).append(sep)
.append(format(getHiTime())).append(sep)
.append(format(getLoTime())).append(sep)
.append(format(getCount()))
.toString();
}

public static String getUUIDHex() {
Properties props = new Properties();
props.setProperty(”seperator”, “”);
UUIDHex uh = new UUIDHex();
uh.configure(props);
return uh.generate();
}

/**
* 实例
*
* @param arg
*/
public static void main(String[] arg) {
System.out.println(UUIDHex.getUUIDHex());
}

}