BS架构下applet,JNI实现对客户端硬件的读取

    技术2022-05-19  21

    声明:本文只是提供了大体的思路,文中所涉及到的代码,不保证都能无错误运行  

    前言

    项目要求连接读卡器读写IC卡。由于项目是用PHP开发,所以刚开始设计的是用PHP连接读卡器(几个人还都没察觉有什么问题,汗)。

    顺便说一下PHP如何读取硬件.

    第一种:开发PHP标准的extension,此种方法开发难度较高,但是后期维护很容易。

    第二种:写个COM组件注册到系统里,然后PHP调用COM组件。此种方法实现比较容易,但是维护较复杂,需要用regsvr32注册COM组件。

    最后讨论决定用第二种比较简单的方法(到这里还是没察觉任何问题。汗)

    路子选好了,说干就干,热火朝天的忙了两天,用VB写了个COM组件,然后注册到系统里.很显然功能实现了(欣喜若狂,但是还未察觉任何不妥).于 是打包交给组长,组长试了试,也没什么问题。都以为就这么完事了。孰料第二天,组长突然提出了一个很严重的问题,PHP是在服务器端运行的,而读卡器是连 接在客户端的。之前测试的客户端服务器端都在一台电脑上面,真正项目部署是不可能这样.瞬间就石化了,屁颠屁颠忙了两天,忙的跟真的一样

    可是项目还是要做的,生活还是要过的,赶鸭子也是得上架的。苦思冥想十几分钟.既然是客户端读取硬件,必须用浏览器的插件实现,于是瞬间想到了老本行Java里面的applet.觉得这是一条可以走通的路,没办法,摸着石头过河也得过啊。一步一步走吧.

     

    最简单的applet

    第一步,首先弄明白applet怎么实现(说实话,以前真没写过applet).网上查了些资料,编码也不是很难.代码大致如下(由于是在家写的,代码在公司,所以只能凭记忆大概写点了)

    //Java代码

     

    // 包含包的部分省略,记不得了 class ICCARD extend Applet{ public void paint(Graphics g) g.drawString( " Hello Applet " , 50 , 50 ); }

     

     

    //HTML代码

     

    < html > < body > < applet id ="applet" code ="ICCARD.class" width ="500" height ="500" /> </ body > </ html >

     

     

    记得要javac ICCARD.java.然后把html和class放在同一目录下,浏览器打开,根据你的安全设置,会有不同提示,一般IE默认会提示是不安装 Activex控件,同意即可,Firefox则没有提示,直接就运行了(个人还是比较喜欢Firefox的,尤其里面的firebug).

     

    applet和JavaScript合体

    大多数情况下,我们的Applet都不可能只有一个方法,所以如何执行我们想要的方法呢,换句话说就是如何和JavaScript交互呢。

    //Javascript Code

    var applet = document.getElementById( " applet " ); applet.hello();

    其实Javascript和Applet交互就这么简单(注意,这里是JavaScript调用Applet,不是Applet调用JS.关于Applet中调用JS,可自己搜索相关资料).

    如果hello()有返回值的话,JS还可以接受返回值。

     

    最简单的JNI

    第一个主要模块Applet已经基本实现,按照我们的设想,applet下载到客户端执行,然后读取客户端的dll.貌似行的通.所以接下来的关键 问题就是Java 如何读取dll,换句话说就是Java如何和其他语言协同工作,很明显,JNI闪亮登场。接下来主要就是解决JNI的问题,所以我们暂且抛开applet 的环境,用最最简单的方法调试JNI.

    //Java Code

    public class Hello{ //必须要先把我们的dll库load进来,我们的native方法才能运行 static { System.loadLiberary( " Hello " ); } public native void hello(); }

     

    Java要做的就这么多,具体的实现就交给C++了(我用的是VC6.0)

    第一步生成class文件 javac Hello.java

    第二部生成C++引用的头文件javah Hello.生成文件内容如下

    代码 /* DO NOT EDIT THIS FILE - it is machine generated */ #include < jni.h > /* Header for class Hello */ #ifndef _Included_Hello #define _Included_Hello #ifdef __cplusplus extern " C " { #endif /* * Class: Hello * Method: hello * Signature: ()V */ JNIEXPORT void JNICALL Java_Hello_hello (JNIEnv * , jobject); #ifdef __cplusplus } #endif #endif

    这里我们只关心那个方法,把方法copy到下面的c++source file里面

     

    第三部具体的C++代码实现.

    新建dll工程->新建c++ source file

    //c++ code

    #include < stdio.h > #include < jni.h > #include < Hello.h > //从Hello.h拷贝来的 JNIEXPORT void JNICALL Java_Hello_hello (JNIEnv * , jobject){ printf( " Hello C++ " ); }

    不用怀疑c++代码就这么简单,但是jni.h和Hello.h的路径要设置正确,编译器才能找到这两个文件

    jni.h在jdk/include下

    jni.h里面还会包含一个jni_md.h,在jdk/include/win32目录下

    Hello.h就随便你放了

    把以上提到的路径加到C++的库目录下.工具->选项->目录.

    然后编译dll,就会生成我们需要的dll文件.

    这个dll的文件名,就是我们在java代码中System.loadLiberary("Hello")里面的这个参数,大小写无所谓,后缀名不需要,会自动识别。

    System.loadLiberary()这个方法会默认搜索几个目录,把dll放在正确的目录下,JVM就能自动加载了.然后我们的native就能运行了。

    java Hello,控制台输入Hello C++;

    这里只是提供了一个JNI最简单的实现,更详细的JNI编程,如参数传递等请参考http://www.docin.com/p-46800196.html

     

    Applet和JNI合体

    上面我们的JNI是没有在applet环境下运行的,下面就要把Applet和JNI结合起来了。

    //Java code

    import java.applet.Applet; public Hello extend Applet{ // 从Applet继承 public void init(){ System.loadLiberary( " Hello " ); } public native String hello(); }

    //C++ code

     

    #include < stdio.h > #include < jni.h > #include < Hello.h > // 从Hello.h拷贝来的 JNIEXPORT jstring JNICALL Java_Hello_hello (JNIEnv * env, jobject){ return env -> NewStringUTF( " Hello JavaScript " ); }

    这里涉及到了传参的问题,详细内容请参考上面给出的网址。

    //JS Code

    var applet = document.getElementById( " applet " ); alert(applet.hello());

    这里几个主要的代码就这样,至于其他次要的工作,由于时间有限,就不再一一赘述.

     

    到了这里我们发现,由于Applet是在客户端运行的,那么我们如何把我们的dll也放到客户端呢.

    首先,试试把class文件和dll打包一起发到客户端行不行呢,jar -cvf Hello.jar *.class *.dll.注意,打包的话<applet>标签里面就要加个archive属性,archive="Hello.jar".

    打开浏览器,结果失败了,同志们就不要再试了。

    那么最简单的方法,就是手动拷贝到客户端(至于自动用Applet下载到客户端,我没有尝试过,有兴趣可以试试).把我们的dll拷贝到system32下.(由于我们项目的客户端比较少,比较单一,所以还不至于被骂).

    再打开浏览器运行,如果前面步骤都没问题的话,浏览器应该会报access denied错误.紧要关头,突现离奇错误,为虾米呢?请听下文分解.

     

    applet数字签名

    其实动动脚指头就能想得通了,为了安全,applet不可能具有肆无忌惮的权限,想读就读,想写就写,那样网络世界就乱套了。applet默认的安 全机制是阻止对客户端的任何操作的(必须阻止).那我们就要想办法允许applet有读写权限。又接着去网上查了许多资料,发现有的人说可以读写,有的人 说不能(我的心凉了一半啊,都进行到这一步了,我容易吗我),但是不管怎样,只要还有希望,就要继续努力.按照网友们给出的方法,对applet的jar 包签名.

    首先把.class都打成jar包:jar -cvf iccard.jar *.class

    然后用jdk/bin目录下的keytool生成.store文件:keytool -genkey -keystore iccard.store -alias Mission -validity 999.这样会提示你输入一些信息,最后时候确认即可.

    最后用jdk/bin下的jarsigner给jar文件签名 jarsigner -verbose -keystore iccard.store iccard.jar Mission.会提示你输入密码,然后会打印出签名的过程(verbose参数的作用).

    关于applet签名的过程,可以去百度“applet签名”,由于以上只凭记忆写的,所以难免有不准的地方。

    好了,这下我们的jar包可厉害了,它已经不是一般的jar包了,它是一个签过名的jar包,一个具有对客户端读写权限的jar包.好了,现在让我 们满怀期待的打开浏览器,嗯,IE提示是否允许Active,允许,然后就跳出窗口让我们验证签名,允许。。然后,OMG,顺利执行了,然后,然后就没有 然后了。

     

    总结:

    虽然功能可以实现,可以跨浏览器,但是缺点也是比较多的。

    1.首先,要把我们的dll拷贝到客户端。

    2.其次,客户端还要装JRE

    由于我们的客户端用IC读卡器的比较少,所以这至少还是一个行之有效的方案,如果面向客户比较多的话,我觉得可一开发BHO(浏览器帮助对象).这已经不是本文讨论范围.


    最新回复(0)