 | Уровень сложности: средний Билал Сиддикви, внештатный консультант, WaxSys
13.10.2009 Билал Сиддикви продолжает эту серию статей, показывая, как использовать Acegi для защиты Java™ Server Faces (JSF)-приложений. В ней рассказывается, как настроить JSF и Acegi для совместной работы в сервлет-контейнере, и рассматривается взаимодействие компонентов JSF и Acegi друг с другом.
Первые три части этой серии статей знакомят с использованием инфраструктуры безопасности Acegi (Acegi Security System) для защиты корпоративных Java-приложений:
- Первая часть показывает, как использовать встроенные фильтры Acegi для реализации простой системы безопасности, основанной на URL.
- Вторая часть показывает, как написать политику контроля доступа, сохранить ее в сервере каталогов LDAP и настроить Acegi для взаимодействия с LDAP-сервером для реализации этой политики.
- Третья часть показывает, как использовать Acegi для защиты доступа к экземплярам Java-классов в корпоративных приложениях.
В этой четвертой статье рассказывается, как использовать Acegi для защиты JavaServer Faces (JSF)-приложений, работающих в сервлет-контейнере. Сначала объясняется, какие возможности предоставляет Acegi для этой задачи, и развеиваются некоторые общие заблуждения об использовании Acegi и JSF. Затем в статье представлен простой файл web.xml, который можно использовать для развертывания Acegi для защиты JSF-приложения. Далее статья переходит к внутренней структуре Acegi и JSF-компонентов для понимания последовательности событий, которые происходят при установке файла web.xml и при обращении пользователя к JSF-приложению. Статья завершается примером JSF-приложения, защищенного Acegi.
Усиление безопасности без создания Java кода
Вернемся к первому в этой серии примеру Acegi-приложения (см. раздел "Простое приложение Acegi" в первой части). Это приложение использует Acegi для реализации следующих функций безопасности:
- Выдача страницы для входа в систему, когда неаутентифицированный пользователь пытается обратиться к защищенному ресурсу.
- Перенаправление авторизованного пользователя напрямую к затребованному защищенному ресурсу.
- Выдача страницы "отказано в доступе" если пользователь не авторизован для доступа к защищенному ресурсу.
Важно помнить, что не требуется писать никакого Java-кода для получения этих возможностей. Необходимо только настроить Acegi. Также возможно получить эти возможности от Acegi в JSF-приложении без написания всякого Java-кода.
Развеиваем заблуждения
Некоторые авторы, по-видимому, считают, что интеграция Acegi с JSF требует, чтобы JSF-приложение предоставляло страницу для входа в систему (см. раздел Ресурсы). Это неправда. Это обязанность Acegi - выдавать страницу для входа в систему, когда она потребуется. Также Acegi гарантирует, что страница для входа в систему будет выдана только один раз в течение защищенного сеанса. Аутентифицированные и авторизованные пользователи могут запросить защищенный ресурс без повторного выполнения процедуры входа в систему.
Если использовать JSF для выдачи страницы входа в систему, то возникают две основные проблемы:
- Не используются возможности Acegi для обслуживания страницы для входа в систему, когда она требуется. Необходимо написать Java-код для реализации всей логики, обслуживающей страницу для входа в систему.
- Необходимо написать некоторое количество Java-кода для передачи параметров, идентифицирующих пользователя, например, имени пользователя и пароля со страницы для входа в систему JSF в Acegi.
Acegi задумана как альтернатива написанию кода, обеспечивающего безопасность в Java. Использование JSF для обслуживания страницы для входа в систему противоречит этой цели и приводит к возникновению множества проблем с интеграцией JSF и Acegi, большинство из которых связаны с фактом, что Acegi предназначена для реализации настраиваемой безопасности. Если использовать JSF для выполнения работы Acegi, могут возникнуть проблемы.
Оставшаяся часть этой статьи объясняет и демонстрирует, как можно разрабатывать JSF-приложения независимо от Acegi и в дальнейшем конфигурировать Acegi для защиты JSF-приложений без необходимости написания какого-либо Java-кода. Начнем с изучения файла web.xml, который можно развернуть для защиты JSF-приложения.
Развертывание Acegi для защиты JSF-приложения
В листинге 1 показан файл web.xml (часто называемый дескриптором развертывания - deployment descriptor), который можно использовать для развертывания Acegi с целью защиты JSF-приложений, работающих внутри сервлет-контейнера (такого как Apache Tomcat):
Листинг 1. Файл web.xml file для установки Acegi и JSF в сервлет-контейнер
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/acegi-config.xml</param-value>
</context-param>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>
com.sun.faces.config.ConfigureListener
</listener-class>
</listener>
<!-- сервлет Faces (основной компонент JSF-приложения) -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup> 1 </load-on-startup>
</servlet>
<!-- URL-шаблон, соответствующий Faces сервлету -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<!-- конфигурация фильтра Acegi -->
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>
org.acegisecurity.util.FilterToBeanProxy
</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>
org.acegisecurity.util.FilterChainProxy
</param-value>
</init-param>
</filter>
<!-- URL-шаблон, соответствующий фильтру Acegi -->
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
|
Важно отметить, что листинг 1 содержит следующие теги:
- Три тега
<context-param> (параметр контекста)
- Два тега
<listener> (обработчик событий)
- Один тег
<filter> (фильтр)
- Один тег
<servlet> (сервлет)
- Один тег
<servlet-mapping> (URL-шаблон для сервлета)
- Один тег
<filter-mapping> (URL-шаблон для фильтра)
Далее рассматривается назначение каждого из этих тегов в приложении JSF-Acegi.
Передача параметров контекста в Acegi и JSF
Каждый тег <context-param> в листинге 1 определяет параметр, который требуется Acegi или JSF во время запуска или работы. Первый параметр contextConfigLocation определяет местоположение конфигурационного XML файла Acegi.
Параметры javax.faces.STATE_SAVING_METHOD и javax.faces.CONFIG_FILES требуются для JSF. Параметр javax.faces.STATE_SAVING_METHOD определяет, как предполагается хранить состояние представления JSF-страницы: на клиенте или на сервере. По умолчанию эталонная реализация JSF от Sun хранит представления JSF на сервере.
Параметр javax.faces.CONFIG_FILES определяет местоположение конфигурационных файлов, требующихся для JSF. Подробная информация о конфигурационных файлах выходит за рамки этой статьи. (В разделе Ресурсы представлены ссылки на материалы по этой теме.)
Конфигурация обработчиков для Acegi и JSF
Теперь рассмотрим два тега <listener> в листинге 1. Теги <listener> определяют классы обработчиков, которые прослушивают и обрабатывают определенные события, возникающие во время запуска или исполнения JSP или сервлет-приложения. Параметры тега могут включать обработчики следующих событий:
- Сервлет-контейнер создает новый контекст сервлета (servlet context) при запуске JSP или сервлет-приложения. Это событие возникает каждый раз, когда запускается JSP или сервлет-приложение.
- Сервлет-контейнер создает новый объект запроса для сервлета (servlet request). Это событие случается каждый раз, когда контейнер получает HTTP-запрос от клиента.
- Устанавливается новый HTTP-сеанс с пользователем. Это событие происходит, когда запрашивающий клиент устанавливает HTTP-сеанс с сервлет-контейнером.
- Добавляется новый атрибут в объекты контекста сервлета, запроса сервлета или HTTP-сеанса.
- Изменяется или удаляется существующий атрибут из объектов контекста сервлета, запроса сервлета или HTTP-сеанса.
Тег <listener> - это механизм расширения, позволяющий приложению, работающему внутри сервлет-контейнера выполнять обработку в связи с определенными событиями. В спецификации сервлетов определены несколько интерфейсов, которые должны реализовывать классы-обработчики для обработки событий.
Например, в инфраструктуре Spring Framework реализуется интерфейс обработки событий для контекста сервлета - javax.servlet.ServletContextListener. В инфраструктуре Spring, класс, реализующий этот интерфейс, называется org.springframework.web.context.ContextLoaderListener. Можно увидеть этот класс-обработчик в первом теге <listener> в листинге 1.
Точно так же JSF предоставляет класс com.sun.faces.config.ConfigureListener, который реализует несколько интерфейсов для обработки событий. Класс ConfigureListener можно найти во втором теге <listener> в листинге 1.
Далее в статье рассматриваются различные интерфейсы для обработки событий и обработку данных, выполняющуюся внутри классов обработчиков событий Acegi и JSF (см. разделы "Запуск JSF-Acegi приложения" и "Обработка запроса к JSF-странице, защищенной Acegi").
Конфигурирование и привязка к URL-шаблонам фильтров сервлетов
Теперь обратите внимание на тег <filter> в листинге 1. Сервлет-приложения используют фильтры для предварительной обработки входящих запросов, прежде чем запрашиваемый сервлет сможет обработать их. Acegi использует фильтры сервлетов для аутентификации пользователей перед обработкой запроса.
В теге <filter> в листинге 1 есть дочерний элемент <filter-class>, определяющий класс org.acegisecurity.util.FilterToBeanProxy. Класс FilterToBeanProxy - это один из компонентов Acegi. Этот класс реализует интерфейс, javax.servlet.Filter, который входит в спецификацию сервлетов. В интерфейсе javax.servlet.Filter есть метод doFilter(), который вызывается сервлет-контейнером при получении запроса.
Также в листинге 1 у тега <filter> есть другой дочерний элемент <init-param>. Тег <init-param> определяет параметры, необходимые для создания экземпляра класса FilterToBeanProxy. Как видно из листинга 1, классу FilterToBeanProxy необходим только один параметр - объект класса FilterChainProxy. Класс FilterChainProxy представляет полную цепочку фильтров Acegi, обсуждавшихся в первой части (см. раздел "Security Filters"). Метод doFilter() класса FilterToBeanProxy использует класс FilterChainProxy для запуска цепочки фильтров безопасности Acegi.
Тег <filter-mapping> в листинге 1 определяет ULR запроса, при котором вызывается FilterToBeanProxy. В данном примере все JSF-страницы просто привязаны к классу Acegi FilterToBeanProxy. Это значит, что управление автоматически переходит в метод FilterChainProxydoFilter(), когда пользователь пытается получить доступ к JSF-странице.
Конфигурация JSF-сервлета
Тег <servlet> в файле web.xml определяет сервлет (в данном случае - JSF-сервлет), который можно вызывать по определенному URL. Тег <servlet-mapping> определяет этот URL. Практически все JSP- или сервлет-приложения содержат эти два тега, так что нет необходимости их подробного обсуждения. (В разделе Ресурсы приведены ссылки на материалы, в которых рассматриваются основы программирования Java-сервлетов.)
К этому моменту были рассмотрены все теги в файле web.xml, необходимые для развертывания Acegi для защиты JSF-приложения. Также было показано, как обработчики событий, сервлеты и фильтры взаимодействуют друг с другом. Как можно предположить, если установить файл web.xml из листинга 1 в сервлет-контейнер, Acegi и JSF попробуют выполнить определенные действия в следующих двух случаях:
- при запуске приложения;
- когда приложение получает запрос к JSF-странице.
В следующих двух разделах разбираются последовательности событий, происходящих в этих двух случаях.
Запуск JSF-Acegi приложения
На рисунке 1 показана последовательность событий, которые происходят при запуске JSF-Acegi приложения:
Рисунок 1. Последовательность событий при запуске JSF-Acegi приложения
Более подробно последовательность событий, изображенная на рисунке 1, описана ниже:
- Сервлет-контейнер создает экземпляры всех обработчиков, сконфигурированных в файле
web.xml.
- Сервлет-контейнер регистрирует класс Acegi
ContextLoaderListener в качестве класса-обработчика, который реализует интерфейс javax.servlet.ServletContextListener. Этот интерфейс ServletContextListener содержит два важных метода: contextInitialized() и contextDestroyed():
- Метод
contextInitialized() получает управление, когда инициализируется контекст сервлета.
- Сходным образом метод
contextDestroyed() вызывается при удалении контекста сервлета во время завершения работы приложения.
- Сервлет контейнер регистрирует класс JSF
ConfigureListener в качестве другого обработчика событий. Класс JSF ConfigureListener реализует сразу несколько интерфейсов для обработки событий, таких как ServletContextListener, ServletContextAttributeListener, ServletRequestListener и ServletRequestAttributeListener. Методы интерфейса ServletContextListener уже рассматривались. Остальные интерфейсы - это:
ServletContextAttributeListener, содержащий три метода: attributeAdded(), attributeRemoved() и attributeReplaced(). Соответственно эти методы получают управление, когда атрибут добавляется в контекст сервлета, удаляется из контекста сервлета или заменяется новым атрибутом. Метод attributeReplaced() получает управление на шаге 8 в последовательности действий при обработке запроса к JSF-странице, защищенной Acegi.
ServletRequestListener, который содержит методы, получающие управление когда новый объект запроса сервлета создается или удаляется. Этот объект запроса для сервлета служит оболочкой для запроса от пользователя.
ServletRequestAttributeListener, содержащий методы, которые получают управление, когда атрибут добавляется, удаляется или заменяется в запросе сервлета. Дальше в статье обсуждается обработка, которую выполняет класс JSF ConfigureListener, когда новый объект запроса сервлета создается на шаге 3 последовательности действий при обработке запроса к JSF-странице, защищенной Acegi.
- Сервлет-контейнер создает объект контекста сервлета, который служит оболочкой для ресурсов приложения (таких как JSP-страницы, Java-классы и параметры инициализации приложения) и позволяет всему приложению получать доступ к ресурсам. Все другие компоненты JSF-Acegi приложения (обработчики, фильтры и сервлеты) хранят информацию о ресурсах приложения в форме атрибутов в объекте контекста сервлета.
- Сервлет-контейнер извещает компонент Acegi
ContextLoaderListener об инициализации контекста сервлета вызовом метода contextInitializated() интерфейса ContextLoaderListener.
- Метод
contextInitialized() анализирует конфигурационный файл Acegi, создает контекст Web-приложения для JSF-Acegi приложения и создает экземпляры всех фильтров безопасности и Java beans-компонентов, настроенных в конфигурационном файле Acegi. Эти экземпляры фильтров затем используются при аутентификации и авторизации, когда JSF-приложение получает запрос от клиента (см. описание создания контекста Web-приложения, относящееся к рисунку 1 в третьей части серии).
- Сервлет-контейнер оповещает компонент JSF
ConfigureListener об инициализации контекста сервлета вызовом метода contextInitialized().
- Метод
contextInitialized() проверяет все Java beans-компоненты, управляемые JSF и определенные в конфигурационных файлах JSF, чтобы убедиться, что для каждого bean-компонента существуют Java-классы.
- Сервлет-контейнер проверяет файл
web.xml на предмет наличия любых сконфигурированных фильтров. Например, в файле web.xml в листинге 1 содержится фильтр Acegi с названием FilterToBeanProxy, экземпляр которого сервлет-контейнер создает, инициализирует и регистрирует в качестве фильтра. Теперь Acegi готова для обработки входящих запросов на предмет аутентификации и авторизации.
- Сервлет-контейнер создает экземпляр Faces сервлета, который начинает ожидать входящих запросов от пользователей.
В следующем разделе рассматривается последовательность событий, возникающих, когда JSF-Acegi приложение получает запрос от пользователя.
Обработка запроса к JSF-странице, защищенной Acegi
Ранее было рассказано, как настроить Acegi для защиты JSF-приложения. Также была рассмотрена последовательность событий, происходящих при запуске JSF-Acegi приложения. В этом разделе описывается, как компоненты JSF и Acegi работают вместе внутри инфраструктуры сервлет-контейнера, когда пользователь отправляет запрос к JSF-странице, защищенной Acegi.
На рисунке 2 представлена последовательность событий, происходящих, когда пользователь отправляет запрос к JSF-странице, защищенной Acegi:
Рисунок 2. Совместная работа JSF и Acegi для выдачи JSF-страницы клиенту
Подробно последовательность событий, изображенных на рисунке 2, описана ниже:
- Сервлет-контейнер создает объект запроса сервлета, представляющий запрос от пользователя.
- Если вернуться к шагу 3 в последовательности действий при запуске JSF-Acegi приложения, то там говорится, что класс JSF
ConfigureListener реализует интерфейс ServletRequestListener. Это значит, что ConfigureListener обрабатывает события, относящиеся к созданию и удалению объектов запроса сервлета. Поэтому сервлет-контейнер вызывает метод requestInitialized() класса ConfigureListener.
- Метод
requestInitialized() готовится к выполнению жизненного цикла запроса, как это описано в спецификации JSF. Подготовка включает в себя проверку того, что для запроса существует Faces-контекст (Faces context - контекст JSF-компонентов). Faces-контекст служит оболочкой для ресурсов приложения, которые позже потребуются Faces-сервлету для выполнения жизненного цикла JSF. Faces-контекст отсутствует, если этот запрос является первым в новом сеансе взаимодействия с пользователем. В этом случае метод requestInitialized() создает новый Faces-контекст.
- Сервлет-контейнер проверяет, имеется ли в запросе пользователя какая-либо информация о состоянии. Если сервлет-контейнер не находит информации о состоянии, он предполагает, что этот запрос является первым в новом сеансе взаимодействия с пользователем, и создает для пользователя объект HTTP-сеанса. Если сервлет-контейнер обнаруживает, что запрос содержит информацию о состоянии (например, cookie или информацию о состоянии в строке URL), он восстанавливает предыдущий сеанс взаимодействия с пользователем, основываясь на полученной информации о сеансе.
- Сервлет-контейнер сопоставляет URL запроса с URL-шаблоном, определенным внутри дочернего элемента
<url-pattern> тега <filter-mapping> в дескрипторе развертывания. Если URL-запроса соответствует URL-шаблону, то сервлет-контейнер вызывает компонент Acegi FilterToBeanProxy, зарегистрированный как фильтр сервлета на шаге 9 рисунка 1.
FilterToBeanProxy использует класс FilterChainProxy для прохождения по всей цепочке фильтров безопасности Acegi. Фильтры Acegi автоматически проверяют объект HTTP-сеанса, созданный на шаге 4, для проверки того, что запрашивающий клиент уже аутентифицирован. Если Acegi обнаруживает, что пользователь не аутентифицирован, она выдает страницу для входа в систему. В противном случае она напрямую обращается к процессу авторизации, описанному в разделе "Configuring the interceptor" второй части.
- Acegi обновляет контекст сервлета, добавляя информацию об аутентифицированном сеансе взаимодействия с пользователем.
- Сервлет-контейнер извещает метод
attributeReplaced() класса JSF ConfigureListener, что контекст сервлета был обновлен. Компонент ConfigureListener проверяет, были ли изменены какие либо из JSF Java Beans-компонентов. Если обнаруживаются какие-либо изменения, то Faces-контекст обновляется соответствующим образом. Однако в этом случае во время аутентификации Acegi не изменяет Java beans-компоненты, управляемые JSF, так что ConfigureListener не выполняет никакой работы во время вызова этого метода.
- Если процесс авторизации проходит успешно, то управление передается в Faces-сервлет, который выполняет жизненный цикл JSF и готовит ответ для отправки обратно клиенту.
Теперь, когда было рассмотрено, как JSF и Acegi работаю вместе для обработки JSF-запроса, пришло время на практике изучить совместную работу JSF и Acegi.
Пример JSF-Acegi приложения
Материалы для скачивания, представленные в этой статье (см. раздел Загрузки), содержат пример JSF-Acegi приложения - JSFAcegiSample, в котором демонстрируется простая интеграция Acegi и JSF. В примере используется файл web.xml, приведенный в листинге 1.
Для установки примера приложения необходимо выполнить два действия из раздела "Развертывание и запуск приложения" в первой части. Также потребуется загрузить и разархивировать файл jsf-1_1_01.zip с сайта Sun JSF (см. раздел Ресурсы), а затем скопировать файлы из архива jsf-1.1.X.zip в папку WEB-INF/lib в каталоге JSFAcegiSample.
Пример приложения можно вызвать, введя в Web-браузере следующий URL: http://localhost:8080/JSFAcegiSample. Пример JSFAcegiSample отображает стартовую страницу, содержащую ссылки на защищенные ресурсы и страницу для входа в систему. Все защищенные компоненты разработаны с использованием JSF-компонентов, тогда как Acegi предоставляет страницу для входа в систему и выполняет аутентификацию и авторизацию.
Заключение
В этой статье рассказывается, как настроить Acegi для защиты JSF-приложений. Также подробно разобрано, как JSF и Acegi компоненты работают вместе внутри инфраструктуры сервлет-контейнера. В конце приведен пример запуска JSF-Acegi приложения.
Есть еще несколько аспектов, касающихся реализации безопасности JSF-приложений с помощью Acegi, которые стоит рассмотреть. В следующей статье этой серии рассматривается, как использовать Acegi для организации защиты доступа к Java Beans-компонентам, управляемым JSF.
Загрузка | Описание | Имя | Размер | Метод загрузки |
|---|
| исходный код для этой статьи | j-acegi4-source.zip | 15KB | HTTP |
|---|
Ресурсы Научиться
- Securing Java applications with Acegi, Part 4: Protecting JSF applications1: (EN) оригинал статьи.
- Securing Java applications with Acegi: эта серия статей знакомит с использованием системы безопасности Acegi для защиты корпоративных Java-приложений.
- Securing Java applications with Acegi, Part 1: Architectural overview and security filters (Bilal Siddiqui, developerWorks, март 2007 г.): эта статья знакомит с архитектурой и компонентами системы безопасности Acegi.
- Securing Java applications with Acegi, Part 2: Working with an LDAP directory server (Bilal Siddiqui, developerWorks, май 2007 г.) : в этой статье демонстрируется Acegi с серверами каталогов.
- Securing Java applications with Acegi, Part 3: Access control for Java objects (Bilal Siddiqui, developerWorks, сентябрь 2007 г.) : в этой статье показывается, как организовать контроль за доступом к Java объектам.
- Acegi Security System: Web-сайт Acegi - первое место, куда стоит обратиться за информацией.
- JSF and Acegi (EN): в этой записи на MyFaces WIKI, посвященной интеграции JSF и Acegi, рассматривается использование bean-компонентов JSF для защиты JSF-приложений.
- Another Faces in your Face: Acegi/JSF solution addendum (Tony L. Kerz, Fair Trade Java Stack Blog, февраль 2006 г.) и Integrating Acegi with JSF (Victor Tatai, sometimes i feel like screaming..., октябрь 2005 г.): в этом блоге обсуждается стратегия интеграции Acegi и JSF.
- ACEGI JSF Components hit the stores (Блог Cagatay Civici, январь 2006 г.) (EN): записи в этом блоге посвящены разработке JSF-компонентов для аутентификации и авторизации с помощью Acegi.
- Apache MyFaces forum (EN): на этом форуме обсуждается использование специальных тегов JSF для интеграции JSF и Acegi.
- Story of a Servlet: An Instant Tutorial (Mark Andrews, java.sun.com) (EN): статья об основах программирования Java сервлетов.
- Getting started with JavaServer Faces 1.2 (Richard Hightower, developerWorks, декабрь 2007 г. и январь 2008 г.) (EN): краткий курс из двух частей, знакомящий с последней версией JSF.
- Using JSF technology for XForms applications (Faheem Khan, developerWorks, февраль 2005 г.) (EN): в этой статье подробно разбирается жизненный цикл JSF.
- Раздел Технология Java : сайта developerWorks: сотни статей обо всех аспектах Java-программирования.
Получить продукты и технологии
Обсудить
Об авторе  | |  | Билал Сиддикви (Bilal Siddiqui) является инженером-электронщиком, консультантом по XML и соучредителем WaxSys, компании, чья деятельность направлена на упрощение электронного бизнеса. После окончания в 1995 г. Инженерно-технологического Университета, г. Лахор, и получения степени по электронной технике, он начал разрабатывать программные продукты для промышленных систем управления. В дальнейшем он занимался XML и использовал свой опыт в программировании C++ для разработки Web- и Wap-базируемых инструментов для XML-технологий, серверных парсинговых программных продуктов и служебных приложений. Билал – проповедник передовых технологий и часто публикуется в этой области. |
Выскажите мнение об этой странице
|  |