Openid 2b

Joid. Есть ли для java толковые библиотеки openid

Сегодня я устрою тест еще одному серверу и consumer-у openid. Этот кандидат прибыл с экзотического острова Явы, так что ставить его в один ряд с описанными в прошлой статье библиотеками нельзя. Требования к хостингу у него будут повыше, да и процедура настройки и установки требует знания основных идей из мира java. Библиотека называется joid (полагаю, сокращение от java openid). Технически кандидат обещает, что сможет понять и версию 1.0 и 2.0 протокола openid. В рекламных проспектах говорится о простоте использования (читай, создания собственного сервера или consumer-а использующего функции joid). Домашняя страничка библиотеки http://code.google.com/p/joid/. На ней вы можете почитать куцую документацию, на странице downloads вы можете скачать саму библиотеку в виде архива joid-1.0.2.jar. Толковых описаний в сети "step by step" по использованию библиотеки я не нашел: пара страничек тупо копировали приведенный на странице проекта пример кода и расхваливали ее простоту и малый размер. Так что придется исправлять этот недостаток.

Скачанный архив библиотеки joid-1.0.2.jar я исследовал вдоль и поперек, и получил стойкое ощущение, что чего-то не хватает: в README шли ссылки на отсутствующие в архиве файлы и каталоги. Попытка решить проблему в лоб, создав веб-приложение (для теста я использовал apache-tomcat-6.0.14) поместив в папку lib библиотеки по следующей инструкции:

1. Copy joid.jar, log4j-*.jar, and tsik.jar to your lib directory (so they end up in WEB-INF/lib). 
 2. Add OpenIdFilter to your web.xml (see below for how to add it) 
 бла-бла-бла

Так вот эта попытка закончилась неудачей: java.lang.ClassNotFoundException: org.verisign.joid.consumer.OpenIdFilter. Так что я выкачал весь проект из SVN. В его состав вошли следующие папки:

examples
lib
resources
src
stores
test

В папке lib находились нужные для работы приложения библиотеки: ant-junit.jar, commons-codec-1.3.jar, commons-httpclient-3.1-rc1.jar, commons-lang-2.3.jar, commons-logging-1.1.jar, javax.servlet.jar, junit-3.8.1.jar, log4j-1.2.13.jar, tsik.jar. Из них только последняя библиотека является редкой (я так и не смог выяснить в составе какой другой системы или продукта она идет). Так что выкачивать целиком проект из репозитария не обязательно. Затем я написал сценарий ant выполняющий компиляцию проекта и упаковку его в виде war-архива.

<?xml version="1.0" encoding="windows-1251"?>
 
<project name="joidapp" default="all" basedir=".">
  <target name="init">
 
   <property name="name" value="joidserver" />
   <property name="version" value="1.0" />
 
   <property name="out" value="out" />
   <property name="src" value="src" />
   <property name="lib" value="lib" />
   <property name="configs" value="configs" />
   <property name="examples" value="examples/server" />
 
   <property name="tomcat" location="E:\Program_Files_2\apache-tomcat-6.0.14" />
 
 
   <path id="clp">
    <fileset dir="${lib}">
       <include name="*.jar" />
    </fileset>
   </path>
 
  </target>
 
  <target name="clean" depends="init">
    <delete dir="${out}" quiet="true" />
 
    <mkdir dir="${out}" />
    <mkdir dir="${out}/WEB-INF" /> 
    <mkdir dir="${out}/WEB-INF/lib" /> 
    <mkdir dir="${out}/WEB-INF/classes" /> 
 
    <copy todir="${out}/WEB-INF/lib">
      <fileset dir="${lib}">
        <include name="**/*.jar" />
        <exclude name="**/javax.servlet.jar" />
      </fileset>
    </copy>
    <copy todir="${out}">
      <fileset dir="${examples}">
        <include name="**/*.*" />
      </fileset>
    </copy>
 
    <copy file="${configs}/web.xml" todir="${out}/WEB-INF" />
    <copy file="${src}/log4j.properties" todir="${out}/WEB-INF/lib" />
 
  </target>
 
  <target name="compile" depends="clean">
    <javac srcdir="${src}" destdir="${out}/WEB-INF/classes" fork="true" classpathref="clp">
 
    </javac>
  </target>
 
 <target name="makewar" depends="compile">
    <jar jarfile="${out}/${name}-${version}.war">
      <fileset dir="${out}" />
    </jar>
    <copy todir="${tomcat}/webapps" file="${out}/${name}-${version}.war" />
 </target>
 
 
 <target name="all" depends="makewar" />
</project>

Ничего сложного в файле ant нет, обратите внимание только на пути к каталогам, в том числе каталог для веб-приложений tomcat.

Файл web.xml я разместил в новой папке configs и выглядит он так:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
<filter> 
 <filter-name>OpenIdFilter</filter-name> 
 <filter-class>org.verisign.joid.consumer.OpenIdFilter</filter-class> 
 <init-param> 
   <description>Optional. Will store the identity url in a cookie under "openid.identity" if set to true.</description> 
   <param-name>saveInCookie</param-name> 
   <param-value>true</param-value> 
 </init-param> 
</filter> 
<filter-mapping> 
 <filter-name>OpenIdFilter</filter-name> 
 <url-pattern>/*</url-pattern> 
</filter-mapping>
</web-app>

Как видите, в этом файле просто подключается сервлет-фильтр. В качестве параметра ему, кроме показанного в примере параметра saveInCookie (сохранять ли подтвержденное значение openid-идентификатора в cookie или нет), можно указать список путей, которые не будут обслуживаться этим фильтром.

Устройство сервлета очень простое: в метод doFilter выполняется проверка двух вещей: того, что обслуживаемый адрес не попал в список "блокированных ресурсов" (настраивается в web.xml) и то, что в списке входных переменных есть одна с именем openid.identity - это признак обратного вызова от сервера аутентификации. Если оба эти условия выполняется, то процедура аутентификации завершается и помещаются сведения о ней внутрь cookie и внутрь сессии:

public static final String OPENID_ATTRIBUTE = "openid.identity";
бла-бла-бла
req.getSession
(true).setAttribute(OpenIdFilter.OPENID_ATTRIBUTE, identity);

Ну а в противном случае ничего не происходит.

Все приложение заработало. Пока только в режиме consumer-а. Теперь надо провести тесты на "профпригодность". Для этого я использовал тех самых openid-провайдеров, о которых писал в прошлой статье.

1. Тест с mediawiki пройден успешно.

2. Тест с wordpress провален: переход на страницу провайдера выполнен не был, вместо этого получено сообщение об ошибке " An error occurred! Please press back and try again. ". В лог tomcat-а попало сообщение, что возникла ошибка NullPointerException, но это произошло где-то внутри файла JoidConsumer.java, но вникать в суть дела и пользоваться отладичком желания у меня не было. Спасибо, следующий.

3. Тест с phpMyID (доступен по адресу http://siege.org/projects/phpMyID/). Результаты изрядно удивили. После ввода в форму адреса openid-страницы передо мной появилось окно ввода имени и пароля. Но после того как я ввел там эти данные (правильные данные), обратный переход на страницу joid выполнен не был, а вместо этого меня снова попросили ввести имя и пароль. В ходе этого на заднем фоне firefox мигало какое-то всплывающее окошко, но оно исчезало так быстро, что рассмотреть толком не успевал. После того как мне надоело вводить имя и пароль. Я отказался и : при повторной попытке (с самого начала запустить процесс openid-аунтентификации) я успешно получил сообщение, что эта процедура была пройдена успешно (сам переход на страницу провайдера уже не выполнялся). Напомнило анекдот про двух алкашей и огурец в трехлитровой банке.

4. Тест с "my openIDOO" (загружен из SVN репозитария "svn checkout http://openidoo.googlecode.com/svn/trunk/ openidoo"). Тест пройден не был: получено очень понятное сообщение об ошибке: "An error occurred! Please press back and try again".

5. Тест с "PHP OpenID Server by JanRain, Inc". Тест также не был пройден. На этот раз в отличие от предыдущих тестов поведение было другим. После ввода openid-идентификатора я попал на страницу для ввода имени и пароля, ввел их, но обратный переход на страницу joid выполнен не был.

Выводы: все ужасно, только mediawiki не завалила тест.

Теперь попробую рассмотреть код клиентской части joid (в глубине души теплится надежда, что возможно я "просто не умею готовить сервера openid").

String returnTo = UrlUtils.getBaseUrl(request);
if (request.getParameter("signin") != null) {
 
String id = request.getParameter("openid_url");
 
if (!id.startsWith("http:")) {
   
id = "http://" + id;
 
}
 
String trustRoot = returnTo;
  String s = OpenIdFilter.joid
().getAuthUrl(id, returnTo, trustRoot);
  response.sendRedirect
(s);
}

Это фрагмент кода из файла index.jsp. В нем размещена html-форма для ввода openid-идентификатора в текстовое поле с именем openid_url. Как видите, первым шагом в примере выполняется проверка того, был ли выполнен вызов данного файла из формы или нет. Во втором случае условие не выполняется, а далее идет html-код страницы, формирующей ту самую форму для ввода openid-идентификатора. В противном случае, полученное из формы значение openid приводится в стандартную форму с добавлением (если это необходимо) префикса протокола, а затем оно поступает на вход к сервлету-фильтру OpenIdFilter. Он был подключен в файле web.xml, который я привел выше. В качестве входных параметров передается только адрес openid-идентификатора, а также адрес страницы, на которую необходимо выполнить возврат. Этот адрес они получают с помощью вызова UrlUtils.getBaseUrl(request), который возвращает строку с адресом текущего исполняющегося jsp-файла.

Следующий шаг - проверка, вдруг процедура аутентификации уже была пройдена и тогда извлекается имя пользователя и печатается на странице:

String loggedInAs = OpenIdFilter.getCurrentUser(session);

В случае если пользователь еще не было залогинен, то значение переменной loggedInAs будет равно null.

В целом впечатление от клиентской части библиотеки очень положительное: код действительно компактен и прост. Хотя от необходимости "поработать напильником" я не свободен. В библиотеке реализована возможность выполнять запросы с помощью SRE (Simple Registration Extension) с целью получения таких сведений как nickname, fio, emai, : Так вот, используя только методы сервлета OpenIdFilter сделать это не возможно, нужно вносить правки в код класса JoidConsumer (именно это класс выполняет черновую работу) и изменить методы создания запроса на аутентификацию. С другой стороны библиотека находится в состоянии развития так что ...

Про сервер

Пример сервера joid построен с помощью трех файлов: login.jsp, me.jsp, logout.jsp. Их назначение, соответственно, логин совмещенный с созданием пользователей, затем, персональная страница аутентифицированного пользователя и страница выхода. Скажу сразу использовать этот пример как openid-сервер не возможно: продукт крайне сырой, нет достаточной документации, идущий в примере код сервлета-сервера openid (работает только с хранящимися в памяти данными), попытки привязать источник данных взятый из базы данных натолкнулся на отстуствие документации по использованию класса dbStore, а из названий и параметров методов нельзя было определить как указать ту БД, таблицу (и как она должна выглядеть). Вывод: фтоппку.

Источник — http://black-zorro.com/mediawiki/Openid_2b

Теги: java openid