Как обойти песочницу: Доступ к нативным методам из апплета
[an error occurred while processing this directive](none) [an error occurred while processing this directive](none)[an error occurred while processing this directive] ::[an error occurred while processing this directive](none)
[an error occurred while processing this directive]([an error occurred while processing this directive] Стив Смолл [an error occurred while processing this directive]) [an error occurred while processing this directive](none)
Откройте, как напрямую вызывать Win32 API — без использования IE.
Обзор
Разработчики апплетов часто бывают недовольны ограничениями системы безопасности- песочницой (sandbox). Эти ограничения могут заставить разработчиков, которые хотят получить доступ к нативной функциональности Win32 избрать целиком подход Microsoft. В этой статье Стив объясняет, что такой подход необязателен. Несколько статей показывают как можно использовать подписанные апплеты, которые могут обойти систему безопасности браузеров Netscape Communicator и Internet Explorer. Этат статья развивает такие идеи и показывает, как подписанные апплеты вместе с JNI могут вызывать Win32 API напрямую. Используя LiveConnect, такие апплеты могут даже работать с Win32 API прямо из JavaScript! (2000 слов)
- Создание нативной библиотеки и "класса-обертки" Java, использующего JNI
- Создание апплета
- Создание подписанного jar-файла
- Создание HTML кода с JavaScript
- Доставка апплета
- Заключение
- Об авторе
- Ресурсы
Разработчики апплетов для платформы Win32 иногда хотят вызывать нативные методы Win32 прямо из апплета. Не зная что есть другие возможности, они могут выбрать Active X или JDirect, использующие Internet Explorer на стороне клиента. Это может быть допустимо для некоторых хорошо управляемых интранетов, однако во многих случаях это создает серьезные ограничения. В этой статье я расскажу как можно реализовать подобную функциональность используя Netscape как клиент.
В предыдущих статьях (см. Ресурсы) были освещены как создание подписанных апплетов, которые могут получать доступ к ресурсам вне "песочницы", так и использование нативных методов из Java кода используя JNI. В этой статье мы совместим эти два подхода для того чтобы создать независимый от Microsoft вариант JDirect. Мы также будем использовать LiveConnect (встроен в Netscape Communicator [и частично реализован в последних версиях IE- прим. переводч.]) для вызова нативных методов напрямую из JavaScript.
Несмотря на то, что наши примеры рассматривают исключительно Win32 API, вы можете легко распространить этот подход для запуска нативного кода из апплета на любой платформе поддерживающей Netscape — просто создайте другую версию нативной библиотеки.
Два свойства системы безопасности апплетов в Netscape делает реализацию этой функциональности сложной. Во-первых, нативная библиотека должна существовать в файловой системе клиентского PC. Это требует, чтобы DLL созданная с помощью JNI была загружена на клиентскую машину.
Более коварное свойство требует, чтобы каждый класс, вызывающий нативный метод, был загружен через system class loader, а не applet class loader. Netscape будет сначала искать классы в своем собственном classpath и загружать их через свой system class loader. Если класс не найден, он будет искать в codebase апплета. Если класс загружается из codebase, он будет обработан AppletClassLoader и получит меньше привелегий. Следовательно, класс вызывающий нативные методы должен находится в classpath Netscape в тот момент, когда он впервые загружается апплетом.
Для примера мы вызовем два очень простых метода Win32: GetUserName() и GetComputerName(). GetUserName() возвращает имя пользователя в Windows, а GetComputerName() возвращает имя машины в Windows. Мы можем использовать этот подход для любого метода Win32 API. Я разбил нашу работу на следующие этапы:
- Создание нативной библиотеки и "класса-обертки" Java, использующего JNI
- Создание апплета
- Создание подписанного jar-файла
- Создание HTML кода с JavaScript
- Доставка апплета
Создание нативной библиотеки и "класса-обертки" Java, использующего JNI
Класс-файл, вызывающий нативные методы, должен быть загружен на машину клиента когда апплет стартует. Это стандартное использование JNI хорошо описано во многих статьях (см. Ресурсы). UserInfo класс не должен делать так уж много — он просто содержит 2 заголовка нативных методов:
public class UserInfo { public native String getUserName() throws Error; public native String getComputerName() throws Error; }
Для создания нативного интерфейса мы должны откомпилировать UserInfo, запустить утилиту javah (под Windows) на откомпилированном класс-файле для получения C header. Этот файл создается автоматически и не требует вашего редактирования.
JNI требует использования прототипов функций в header для разработки нативной библиотеки. Следовательно, мы загружаем этот header в среду разработки Windows C (я использовал Microsoft Visual C++ 5.0) и создаем C-файл, реализующий методы, заданные в интерфейсе (в этом случае просто вызов двух методов Win32, описанных выше). C код выглядит так:
Пример 1.
#include <windows.h> #include <jni.h> /* * Class: UserInfo * Method: getUserName * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_UserInfo_getUserName (JNIEnv *env, jobject obj) { char *ptr; char nameBuff[255]; long length; ptr = nameBuff; GetUserName(ptr, &length ); return (*env)->NewStringUTF(env, ptr); } /* * Class: UserInfo * Method: getComputerName * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_UserInfo_getComputerName (JNIEnv *env, jobject obj) { char *ptr; char nameBuff[255]; long length; ptr = nameBuff; GetComputerName(ptr, &length ); return (*env)->NewStringUTF(env, ptr); }
(Неправда-ли, Java намного приятней!)
Теперь воспользуйтесь "create DLL" опцией в Visual C++ и создайте независимую DLL.
Уфф! Самое тяжелое позади. Теперь можно заняться написанием Java-кода.
Создание апплета
Как мы уже говорили, система безопасности Netscape требует выполнения событий в определенном порядке. Вот последовательность, которой мы должны следовать:
- Загрузить апплет
- Загрузить DLL на клиент
- Загрузить обертку нативных методов на клиент
- Загрузить обертку
- Загрузить нативную DLL
- Вызвать нативные методы
UserInfoApplet делает для нас всю работу (см. Ресурсы). Методы installDLL( ) (показан ниже) и installJNIClass( ) загружают DLL и UserInfo класс. Обе эти функции используют метод copyURL() для копирования из определенной URL в указанное место на машине клиента. Я использовал метод из остроумной утилиты copyURL(), описанной в "Java Tip 19: Java makes it easy to copy files from a Web site." Поскольку эти классы должны требовать специальных привелегий от пользователя апплета, мы должны использовать метод enablePrivilege() из Capabilities API Netscape (как показано в следующем фрагменте кода). Этот метод показывает пользователю диалог в браузере, содержащий информацию о том, кто подписал апплет (в данном случае я).

В нижеприведенном случае, пользователя просят предоставить доступ к локальной файловой системе.
Пример 2.
public void installDLL ( ) { try { PrivilegeManager.enablePrivilege("UniversalFileAccess"); } catch (Exception e) { System.out.println ("Can't enable file Privilege!!!"); e.printStackTrace(); } dllDest = findTempDir() + "userinfo.dll"; System.out.println("Copying " + urlSource + dllFileName + " to " + dllDest + " . . . " ); copyURL ( urlSource + dllFileName, dllDest ); }
Похожий метод, installJniWrapperClass(), прописывает класс-обертку в classpath Netscape. Для согласования с системой безопасности Netscape я записал Java класс-файл в CLASSPATH каталог Netscape. Метод parseClassPath() служит для этого.
Заметьте, что я использовал временный каталог Windows для DLL (поддерживается методом findTempDir()). Это будет работать, если использовать System.load(), использующий абсолютный путь для загрузки библиотеки. Если вы используетеSystem.loadlibrary() для этого, система будет искать в каталогах, указанных в Windows PATH, поэтому нужно использовать соответствующий каталог. Использование System.load() и абсолютного пути, видимо, более подходящий подход.
Метод loadLibrary(), который и загружает DLL, должен также работать вне песочницы и запрашивать разрешение через Capabilities API.
Пример 3.
public void loadLibrary () { try { PrivilegeManager.enablePrivilege("UniversalLinkAccess"); } catch (Exception e) { System.out.println ("Can't enable link Privilege!!!"); e.printStackTrace(); } try { System.out.println ("loading library " + dllDest + " . . . "); System.load ( dllDest ); } catch (Exception e) { System.out.println ("Can't load library!!!"); e.printStackTrace(); } catch (Error err) { System.out.println ("ERROR: Can't load library!!!"); err.printStackTrace(); } }
Как только вы загрузили все необходимые файлы и подключили нативную библиотеку, вы можете использовать нативные методы, как показано в примере destroy() ниже. Заметьте, что мы создаем копию класса-обертки при каждом вызове метода — обертка не должна использоватся, пока она находится в classpath Netscape. Если вы объявите класс-обертку как переменную доступа в апплете, на него будет сделана ссылка в момент загрузки апплета и перед тем как обертка будет загружена и доступна, что приведет к исключительной ситуации.
Пример 4.
public String getUserName() { String name; // we can't reference UserInfo class until AFTER it exists on // local classpath! UserInfo info = new UserInfo(); try { PrivilegeManager.enablePrivilege("UniversalLinkAccess"); } catch (Exception e) { System.out.println ("Can't enable link Privilege!!!"); e.printStackTrace(); } try { name = info.getUserName(); } catch (Error er) { er.printStackTrace(); return ("Java Error on getUserName!") ; } return name; }
Конечно, вы можете расширить приведенные классы Java, чтобы использовать любой метод Win32 API или метод из любого нативного API.
Из соображений безопасности, URL откуда загружаются файлы должна быть жестко задана в апплете. Несмотря на то, что использование param tags апплета кажется самым легким и гибким подходом, это создает серьезную брешь в безопасности. Злонамеренный прльзователь может скопировать подписанный jar и просто задать другой URL для DLL или класса. Такой подмененный файл будет выполнятся с привилегиями, обеспеченными для исходного апплета.
Вы можете либо оставить загруженные файлы на машине клиента, или удалить их после завершения работы апплета (вероятно, в методе destroy() апплета)- выбор зависит от вас и конкретной ситуации.
Для того, чтобы откомпилировать эти файлы, вам необходимо установить в classpath класс netscape.security Privilege. Пакеты безопасности Netscape находятся в файле java40.jar в каталоге браузера (посмотрите в Program\Java\Classes). Вы можете либо добавить этот jar в свой classpath или разархивировать его в существующий каталог вашего classpath.
Создание подписанного jar-файла
Этот процесс описан на сайте Netscape (см. Ресурсы), но мы рассмотрим здесь простейшие шаги.
Во-первых, получите сертификат для подписывания объектов от организации вроде VeriSign, затем установите сертификат на локальной машине.
Как только вы это сделаете, используйте утилиту signtool Netscape (доступна бесплатно на сайте Netscape) для создания подписанного jar-файла. Напимер, если база данных сертификатов находится в c:\Program Files\Netscape\Users\steve_small, сертификат называется Steve Small's VeriSign Trust Network ID, и файлы, котрые должны быть запакованы лежат в каталоге UserInfo, комманда signtool должна быть задана так:
signtool -d "c:\Program Files\Netscape\Users\steve_small" -k "Steve Small's VeriSign Trust Network ID" -Z UserInfo.jarUserInfo
Как только jar создан, вам осталось только написать HTML и доставить апплет.
Создание HTML кода с JavaScript
В нашем HTML мы воспользуемся LiveConnect (встроен в Netscape) для запуска нативных методов прямо из JavaScript.
В медленных сетях или при коммутируемом соединении возможно, что JavaScript будет пытатся получить доступ к апплету до его полной загрузки, что может вызвать исключительную ситуацию. Я поместил диалог вначало JavaScript, с просьбой для пользователя дождаться загрузки апплета.
Эта страничка также содержит JavaScript, проверяющий конфигурацию браузера. Я рекомендую обеспечить поддержку как Netscape так и Internet Explorer (IE). Вероятно, лучше всего поместить эту проверку в самом верху HTML. Если это Communicator, переходим на страничку с апплетом, описанным выше; если же это IE, переходим на страничку с апплетом JDirect или ActiveX.
Пример 5.
<HTML> <APPLET CODE="UserInfoApplet.class" WIDTH=150 HEIGHT=25 ARCHIVE="UserInfo.jar"> </APPLET> <SCRIPT LANGUAGE="JavaScript"> alert("wait for applet to load . .."); function getUser () { UserID=document.applets[0].getUserName(); java.lang.System.out.println("user ID: " + UserID); alert ("User Name returned to JavaScript by Java applet: " + UserID); } function getComputer () { ComputerID = document.applets[0].getComputerName(); java.lang.System.out.println("computer ID: " + ComputerID); alert ("Computer Name returned to JavaScript by Java applet: " + ComputerID); } config=navigator.userAgent; windows=false; netscape=false; if (config.indexOf("Mozilla") != -1) netscape=true; if (config.indexOf("Win") != -1) windows=true; if ( (netscape==true) && (windows==true) ) { document.applets[0].installDLL(); document.applets[0].installJniWrapperClass(); document.applets[0].loadLibrary(); java.lang.System.out.println ("invoking getUser in JavaScript . . ."); getUser(); java.lang.System.out.println ("done invoking getUser in JavaScript . . ."); getComputer(); } else alert("only supported on PC/Netscape platform!"); </SCRIPT> <HEAD> <TITLE>Native Methods From Applet</TITLE> </HEAD><BODY> <P> <P> <BOLD> Insert your HTML here . . . </BOLD> </BODY> </HTML>
Доставка апплета
Теперь, когда мы создали наши нативную DLL, JNI Java класс-обертку, подписанный jar, и наш файл HTML, мы должны доставить это на Web-сайт. Просто установите все файлы в вашем document base (вы можете, конечно, создать каталог для кода апплета и задать codebase).
Я рекомендую создать простой пакетный файл для автоматизации всего процесса создания апплета. Я включил такой пример ниже.
Этот пакетный файл компилирует исходные файлы Java, создает подписанный jar архив, копирует все необходимые для доставки апплета файлы в каталог deploy\. (см. Ресурсы, чтобы загрузить файл.)
Нажмите сюда чтобы увидеть апплет в работе.
Я протестировал этот апплет с Communicator 4.04 и заплаткой JDK 1.1 и с предварительной версией AWT 1.1.5 Communicator 4.05.
Заключение
Может быть реализованная функциональность выглядит очень простой, но подход может быть легко расширен для доступа к любым методам Win32 API — или любой другой нативной API на системе клиента.
Несмотря на то, что такая техника дает огромные возможности, существуют и недостатки:
- Процесс разработки становится более сложным
- Два файла должны быть скопированы на машину клиента (по-крайней мере временно) для запуска апплета
- Ранние версии Netscape не поддерживают такую функциональность
- Уменьшается переносимость апплета
- Пользователи должны доверять подписавшему объекты и произвольно обеспечивать привелегии
Все-таки, такой подход является мощной альтернативой решению целиком от Microsoft для доступа к нативной функциональности из апплета.
Об авторе
Стив Смолл работает консультантом в Cambridge Technology Partners. До этого он был в группе Boeing по разработке Java- компонентов для внутрикорпоративного использования. Ранее он преподавал C и C++ и в настоящее время преподает курс Enterprise Java в Boeing, и курсы Java в University of Washington Continuing Education Program. Стив получил степень бакалавра в области электроники в Washington State University и мастера в области разработки ПО в Seattle University. Стив является Sun Certified Java Programmer.
Ресурсы
- Complete source code listing of UserInfoApplet, plus an example batch file to build the applet and create a signed jar
http://www.javaworld.com/jw-10-1998/ apptowin32/jw-10-apptowin32.zip - Sun's Java Native Interface Specification, tutorial, and FAQ provide a very good overview of native methods
http://www.javasoft.com/products/jdk/1.1/ docs/guide/jni/ - See Netscape's list of documentation related to object signing including tutorials, resources, tools, and FAQs
http://developer.netscape.com/docs/manuals/ signedobj/overview.html - Read Rinaldo Di Giorgio's "Use native methods to expand the Java environment" (JavaWorld, July 1997) for a good introduction to using native methods from within Java code using JNI
http://www.javaworld.com/jw-07-1997/ jw-07-javadev.html - Bret Sommers's "Outside the Sandbox" (Java Report Online, February 1998) provides an excellent tutorial describing signed applet creation
http://www.javareport.com/html/ features/archive/9802/somers.shtml - Java Tip 19: "Java makes it easy to copy files from a Web site" (JavaWorld, December 1996) describes a clever utility for copying files from a URL
http://www.javaworld.com/javaworld/ javatips/jw-javatip19.html - VeriSign offers Netscape Class 2 and Class 3 SPCs
http://www.verisign.com - Thawte Certification also offers Class 3 SPCs
http://www.thawte.com - For a very complete treatment of the Java Native Interface, see Essential JNI by Rob Gordon (Prentice Hall, ISBN: 0-13-679895-0)
Reprinted with permission from the October 1998 edition of JavaWorld magazine. Copyright © ITworld.com, Inc., an IDG Communications company.
View the original article at: http://www.javaworld.com/jw-10-1998/ apptowin32/jw-10-apptowin32.html
[an error occurred while processing this directive] Перевод на русский © Сергей Миссан, 2000