Представляем Apache Shiro

Использование инфраструктуры Apache Shiro для аутентификации пользователей Web-приложения

Познакомьтесь с Apache Shiro и просмотрите несколько примеров, которые позволят вам поэкспериментировать с применением Shiro для аутентификации и авторизации пользователей Web-приложения, написанного на языке Groovy.

Натан А. Гуд, инженер по программному обеспечению, начальник отдела

Натан Гуд (Nathan A. Good) живет в регионе Twin Cities (штат Миннесота, США). Н. Гуд профессионально занимается разработкой программного обеспечения, архитектурой программного обеспечения и системным администрированием. Когда Н. Гуд не занят написанием программного обеспечения, он с удовольствием занимается построением ПК и серверов, изучением и освоением новых технологий, а также убеждением своих коллег в необходимости перехода на программное обеспечение с открытым кодом. Натан Гуд лично и в соавторстве написал множество книг и статей, в том числе: Professional Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach, Regular Expression Recipes for Windows Developers: A Problem-Solution Approach, PHP 5 Recipes: A Problem-Solution Approach. Самая новейшая из его работ: Foundations of PEAR: Rapid PHP Development.



09.09.2010

Apache Shiro – это инфраструктура, которую можно использовать для реализации аутентификации и авторизации. В данной статье приводится несколько примеров применения Shiro в Java-приложении и рассматривается порядок применения Shiro в Web-приложении на платформе Grails. Для извлечения максимальной пользы из этой статьи читатель должен обладать навыками создания Java-приложений. Кроме того, на его компьютере должны быть установлены следующие компоненты:

  • A Java 1.6 JDK
  • Grails (для исполнения примеров Web-приложений )

Часто используемые сокращения

  • API: Application programming interface (Интерфейс прикладного программирования, API-интерфейс)
  • HTTP: Hypertext Transfer Protocol (Протокол передачи гипертекстовых файлов)
  • JAR: Java-архив
  • JDBC: Java database connectivity (Интерфейс JDBC)
  • JDK: Java software development kit (Инструментальный комплект для разработки в среде Java)
  • LDAP: Lightweight Directory Access Protocol (Протокол LDAP)

Аутентификация и авторизация

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

Аутентификация - это процесс идентификации личности пользователя. Цель аутентификации пользователя – подтвердить, что он действительно является тем лицом, за которое он себя выдает. В большинстве приложений аутентификация осуществляется с помощью комбинации «имя пользователя/пароль». Если пользователи применяют пароли, являющиеся достаточно трудными для отгадывания, то комбинации «имя пользователя/пароль» обычно достаточно для подтверждения идентичности пользователя. Однако доступны и другие средства аутентификации, такие как отпечатки пальцев, сертификаты и генерируемые ключи.

После того как процесс аутентификации успешно установил идентичность пользователя, на сцену выходит процесс авторизации, призванный ограничить доступ пользователя или предоставить ему соответствующий доступ. К примеру, возможна ситуация, когда процесс аутентификации позволяет пользователю войти в систему, однако процесс авторизации не разрешает этому пользователю выполнять в системе какие-либо действия. Также возможен определенный уровень авторизации для пользователей, которые не проходили аутентификацию.

Чтобы гарантировать своей системе достаточный уровень безопасности, разработчик должен учитывать оба вышеуказанных элемента при планировании модели безопасности своего приложения. Аутентификация (особенно базирующаяся только на имени пользователя и пароле) – это типичная проблема на уровне приложений, поэтому разумно передать эту работы какой-либо инфраструктуре. Соответствующие инфраструктуры обладают определенными преимуществами – они прошли тестирование и обеспечиваются техническим сопровождением. Это позволяет разработчику сосредоточиться на своей бизнес-проблеме, а не заниматься задачей, решение для которой уже найдено.

Apache Shiro – это практичная инфраструктура безопасности, которую разнообразные клиенты могут с успехом применять в интересах своих приложений. Приведенные в этой статье примеры знакомят читателя со Shiro и иллюстрируют решение базовой задачи – аутентификации пользователя.


Знакомство с инфраструктурой Shiro

Shiro – это реализованная на языке Java инфраструктура, которая предоставляет простой в использовании API-интерфейс как для аутентификации, так и для авторизации. Применение Shiro позволяет разработчику обеспечить безопасность своего приложения без написания всего кода с нуля.

Полное решение аутентификации от IBM

В техническом обзоре Enhancing Identity Assurance across all Access Scenarios Cost Effectively with IBM ISS Identity and Access Management Services рассматривается полностью интегрированное и экономически эффективное аутентификационное решение масштаба всего предприятия, охватывающее разные бизнес-приложения и разные сценарии доступа.

Это решение предоставляет высокозащищенную централизованную инфраструктуру аутентификации, которая может быть легко интегрирована с разными приложениями или с разными компонентами ИТ-инфраструктуры посредством своего API-интерфейса или стандартных протоколов аутентификации (RADIUS или LDAP). Решение поддерживает обширный ассортимент аппаратных и программных средств, включая аутентификационные жетоны на базе мобильных телефонов, что обеспечивает максимальную гибкость при выборе конкретного метода аутентификации.

Инфраструктура Shiro обеспечивает аутентификацию с использованием множества разных источников данных и поддерживает концепцию Enterprise Session Management, благодаря чему она идеально подходит для реализации технологии единого входа SSO (Single Sign-On), которая весьма желательна в больших организациях, где пользователями в течение одного дня приходится входить во множество разных систем и работать с ними. Shiro поддерживает следующие источники данных: JDBC, LDAP, Kerberos и Microsoft® Active Directory® Directory Services (AD DS).

Объект Session инфраструктуры Shiro позволяет задействовать сеанс пользователя, даже не имея HttpSession. Применение собственного объекта Session позволяет использовать один и тот же код, даже если этот код не исполняется в Web-приложении. Избавление от необходимости управления сеансами сервера приложений/сервера Web-приложений позволяет использовать Shiro даже в среде командной строки. Другими словами, код, написанный с помощью API-интерфейса Shiro, позволяет разработчику создавать основанные на командной строке приложения для соединения с LDAP-сервером, и этот же код сможет обращаться к LDAP-серверу в Web-приложении.

Загрузка и установка Shiro

Shiro поставляется в виде готовых двоичных дистрибутивов. Вы можете загрузить JAR-файлы Shiro или поместить соответствующие записи в инструмент Apache Maven или Apache Ivy с целью автоматической установки файлов. В данном примере для загрузки JAR-файлов Shiro и других необходимых библиотек применяется инструмент Ivy. При этом используются простые скрипты, показанные в листинге 1.

Листинг 1. Файл Apache Ivy и скрипт Apache Ant
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
    <info organisation="com.nathanagood.examples" module="shirotest" />
    <configurations>
        <conf name="dist" description="Dependency configuration for distribution." />
    </configurations>
    <dependencies>
        <dependency org="commons-logging" name="commons-logging"
            rev="1.1.1" conf="dist->default" />
        <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.5.8"
            conf="dist->default" />
        <dependency org="org.apache.shiro" name="shiro-core" rev="1.0.0-incubating"
            conf="dist->default" />
        <dependency org="org.apache.shiro" name="shiro-web" rev="1.0.0-incubating"
            conf="dist->default" />
    </dependencies>
</ivy-module>

<project name="shiroTestApp" default="usage" basedir="." 
    xmlns:ivy="antlib:org.apache.ivy.ant">
    <property name="project.lib" value="lib" />
    <path id="ivy.task.path">
        <fileset dir="${basedir}/ivy-lib">
            <include name="**/*.jar" />
        </fileset>
    </path>

    <target name="resolve">
        <taskdef resource="org/apache/ivy/ant/antlib.xml" 
            uri="antlib:org.apache.ivy.ant" classpathref="ivy.task.path" />
        <ivy:resolve />
        <ivy:retrieve pattern="${project.lib}/[conf]/[artifact].[ext]" sync="true" />
    </target>

    <target name="usage">
        <echo message="Use --projecthelp to learn more about this project" />
    </target>
</project>

Ссылки на дополнительную информацию об инструменте Ivy приведены в разделе Ресурсы. Если вы не используете инструмент Maven или Ivy, загрузите JAR-файлы Shiro с Web-сайта загрузки, ссылка на который также предоставлена в разделе Ресурсы.

После загрузки библиотек достаточно добавить их к своей переменной среды CLASSPATH. Напишите простой код, показанный в листинге 2, который получает ссылку на текущего пользователя и сообщает, что этот пользователь не аутентифицирован (для представления пользователя воспользуйтесь классом Subject).

Листинг 2. Java-класс ShiroTest
package com.nathanagood.examples.shirotest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ShiroTest {

    private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);

    public static void main(String[] args) {
        // Using the IniSecurityManagerFactory, which will use the an INI file
        // as the security file.
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
            new IniSecurityManagerFactory("auth.ini");

        // Setting up the SecurityManager...
        org.apache.shiro.mgt.SecurityManager securityManager 
            = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        Subject user = SecurityUtils.getSubject();

        logger.info("User is authenticated:  " + user.isAuthenticated());
    }
}

После добавления этого кода создайте файл с именем auth.ini. На данный момент этот файл пуст; он существует только для того, чтобы вы смогли исполнять пример с целью проверки корректности работы программного кода.

После создания указанного файла запустите свой экземпляр. На выходе вы должны увидеть регистрационное INFO-сообщение с информацией о том, что пользователь не совершил вход в систему.

Объект SecurityUtils является единичным; другими словами, разные объекты могут использовать его для получения доступа к текущему пользователю. После успешной настройки SecurityManager вы можете осуществлять вызов SecurityUtils.getSubject() в разных фрагментах своего приложения для получения сведений о текущем пользователе.


Пользовательские жетоны

В терминологии Shiro жетон (token) – это ключ, с помощью которого пользователь входит в систему. UsernamePasswordToken – это базовый типовой жетон, который позволяет задать для пользователя имя и пароль.

Класс UsernamePasswordToken реализует интерфейс AuthenticationToken, позволяющий пользователю получать полномочия и права доступа (идентификационная учетная запись). Жетон UsernamePasswordToken успешно работает для большинства приложений. Кроме того, в случае необходимости вы сможете расширить интерфейс AuthenticationToken, включив в него ваш собственный метод получения полномочий. К примеру, вы можете расширить этот интерфейс, чтобы он предоставлял содержимое файла ключей, который ваше приложение применяет для аутентификации пользователей.


Простая аутентификация

До настоящего момента на нашем простом примере мы продемонстрировали следующие аспекты: запуск компонента Shiro под названием SecurityManager, получение сведений о текущем пользователе, регистрация сведений о том, что данный пользователь еще не был аутентифицирован. Теперь мы используем жетон UsernamePasswordToken и пользовательскую запись, хранящуюся в INI-файле, для демонстрации порядка аутентификации на основе имени пользователя/пароля.

На данный момент учебный файл auth.ini, показанный в листинге 3, содержит пользовательскую запись, которая, в свою очередь, содержит имя и пароль пользователя. В этой записи вы можете задать роли, а также организовать авторизацию для своего приложения.

Листинг 3. Файл auth.ini
[users]
bjangles = dance

Теперь создайте описанный в предыдущем разделе объект UsernamePasswordToken, как показано в листинге 4.

Листинг 4. Использование класса UsernamePasswordToken
// snipped... same as before.
public class ShiroTest {

    private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);

    public static void main(String[] args) {
        // Using the IniSecurityManagerFactory, which will use the an INI file
        // as the security file.
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
            new IniSecurityManagerFactory("auth.ini");

        // Setting up the SecurityManager...
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        Subject user = SecurityUtils.getSubject();

        logger.info("User is authenticated:  " + user.isAuthenticated());
        
        UsernamePasswordToken token = new UsernamePasswordToken("bjangles", "dance");
        
        user.login(token);
        
        logger.info("User is authenticated:  " + user.isAuthenticated());
    }
}

Объект UsernamePasswordToken инициализируется посредством комбинации «имя пользователя/пароль». Затем этот жетон передается в метод login() класса Subject.

Снова запустите данный пример. Обратите внимание, что теперь регистрационное сообщение говорит о том, что пользователь аутентифицирован.

Чтобы удостовериться в том, что программный код работает надлежащим образом и что вы не получаете «ложно-положительного» результата, измените пароль в программном коде или в INI-файле, а затем снова запустите пример. Теперь метод login() выдает исключение IncorrectCredentialsException. В реальном приложении это исключение будет перехвачено вашим программным кодом, что позволит вашему приложению адекватно реагировать в случае предоставления пользователем неправильного пароля.

Если некорректным является имя пользователя, метод login() выдает исключение UnknownAccountException. У вас может возникнуть желание реализовать обработку этого исключения, однако лучше не давать пользователю слишком много информации. Типовой подход в данном случае состоит в том, чтобы избегать любых указаний пользователю на то, что введенное им имя пользователя является допустимым, а некорректным является лишь пароль. Если какое-либо лицо попытается получить доступ методом подбора, вам не следует подсказывать ему, что предполагаемое им имя пользователя является правильным.


Аутентификация с использованием LDAP

LDAP – это протокол для доступа к службе каталогов по TCP/IP-каналу. Такие каталоги могут содержать любой объем информации о пользователе, в т.ч. идентификатор пользователя, контактные сведения, информацию о членстве в группах и т.д. LDAP-каталоги широко применяются, в частности, для создания корпоративных адресных справочников.

AD DS – широко распространенная служба каталогов, применяемая для управления пользователями и группами, – поддерживает протокол LDAP. Инфраструктура Shiro не включает в себя типовую LDAP-зону безопасности, однако содержит объект ActiveDirectoryRealm, который позволяет аутентифицировать пользователей по технологии LDAP. В данном примере используется объект ActiveDirectoryRealm, сконфигурированный в INI-файле на аутентификацию пользователей. Хотя AD DS – это не то же самое, что LDAP, с версией Shiro, которая используется в этой статье, не поставляется никакого типового LDAP-объекта.

Получение в свое распоряжение LDAP-сервера для тестирования этого примера может потребовать значительно больше усилий, чем написание и исполнение самого примера. Если у вас нет доступа к какому-либо серверу AD DS, то в качестве примера реализации LDAP-сервера загрузите и установите продукт Apache Directory. Продукт Apache Directory написан на языке Java. Аналогично, Apache Active Directory Studio – это подключаемый модуль инфраструктуры Eclipse, позволяющий просматривать LDAP-данные. Кроме того, этот продукт поставляется с некоторыми типовыми данными, которые позволяют разработчику быстро написать программный код с известными значениями, не опасаясь каких-либо проблем с данными или с кодом.

Программный код в листинге 5 применяется для аутентификации пользователя, данные о котором хранятся в Apache Directory.

Листинг 5. Аутентификация по LDAP
// snipped...
public class ShiroLDAPTest {

    private static Logger logger = LoggerFactory.getLogger(ShiroLDAPTest.class);

    /**
     * @param args
     */
    public static void main(String[] args) {

        // Using the IniSecurityManagerFactory, which will use the an INI file
        // as the security file.
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
            new IniSecurityManagerFactory("actived.ini");

        // Setting up the SecurityManager...
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        Subject user = SecurityUtils.getSubject();

        logger.info("User is authenticated:  " + user.isAuthenticated());

        UsernamePasswordToken token = 
        new UsernamePasswordToken(
            "cn=Cornelius Buckley,ou=people,o=sevenSeas", "argh");

        user.login(token);

        logger.info("User is authenticated:  " + user.isAuthenticated());
    }
}

За исключением имени INI-файла и имени/пароля пользователя, этот код идентичен коду, применяемому для аутентификации пользователя с помощью записей в INI-файле. Это подобие обуславливается возможностью конфигурированию инфраструктуры Shiro с использованием INI-файла. В листинге 6 показаны записи INI-файла, используемые для настройки Shiro на аутентификацию с помощью Apache Directory.

Листинг 6. Файл actived.ini
[main]
activeDirectoryRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
activeDirectoryRealm.systemUsername = uid=admin,ou=system
activeDirectoryRealm.systemPassword = secret
activeDirectoryRealm.searchBase = o=sevenSeas,ou=people
activeDirectoryRealm.url = ldap://localhost:10389

Примечание. Я с помощью продукта Apache Directory Studio изменил пароль пользователя на такое значение, которое можно поместить в тестовый код, чтобы удостовериться в его работоспособности.


Исполнение Web-приложения на базе Grails

Инфраструктуру Shiro можно применить к двум базовым технологиям Web-приложений. Во-первых, вы сможете просто использовать API-интерфейс Shiro для включения в базовый сервлет той версии программного кода, которая была представлена в данной статье. Во-вторых, вы можете воспользоваться HTTP-фильтрами, поставляемыми вместе с инфраструктурой Shiro. В этом примере демонстрируется вторая технология, поскольку применение фильтров базируется на встроенном сервере Web-приложений и на готовом коде проекта Shiro.

Этот пример показывает, как использовать фильтры в среде Grails. Grails – это проект, помогающий разработчикам максимально быстро писать Web-приложения на Groovy благодаря заранее согласованным конфигурациям. Для получения дополнительной информации по платформе Grails обратитесь к разделу Ресурсы.

Обычно необходимые записи для фильтров Shiro добавляются в файл web.xml в ручном режиме. Однако Grails генерирует файл web.xml при каждом запуске вашего приложения, поэтому у вас нет необходимости модифицировать файл web.xml вручную.

К счастью, инфраструктура Grails поддерживает подключаемые модули, которые внедряются в процесс генерации файла web.xml, что позволяет разработчику принять участие в написании записей в файле web.xml. Для инфраструктуры Grails существует множество подключаемых модулей, в том числе один модуль для Shiro. Опробуйте подключаемый Grails-модуль для Shiro, – он предоставляет несколько новых скриптов, которые вы сможете запускать для создания разных областей и контроллеров.

Если вы предпочитаете добавлять записи и осуществлять конфигурирование самостоятельно, то в качестве альтернативного варианта вы сможете написать свой собственный подключаемый модуль. Инфраструктура Grails существенно облегчает написание новых подключаемых модулей. Для создания подключаемого Grails-модуля, который будет добавлять в ваш файл web.xml записи о необходимых фильтрах Shiro, начните со следующей команды:

> grails create-plugin ShiroWebXml

После того как вы создадите проект подключаемого модуля, отредактируйте файл ShiroWebXmlPlugin.groovy и добавьте в него код, показанный в листинге 7.

Листинг 7. Пример подключаемого модуля
class ShiroWebXmlPlugin {
   
   // snipped plugin details... 

   def doWithWebDescriptor = { xml ->

       def filterElement = xml.'filter'
       def lastFilter = filterElement[filterElement.size() - 1]

       lastFilter + {
           'filter' {
               'filter-name'("ShiroFilter")
               'filter-class'("org.apache.shiro.web.servlet.IniShiroFilter")
               'init-param' {
                   'param-name'("config")
                   'param-value'("\n#config")
               }
           }
       }

       def filterMappingElement = xml.'filter-mapping'
       def lastFilterMappingElement = 
           filterMappingElement[filterMappingElement.size() - 1]

       lastFilterMappingElement + {
           'filter-mapping' {
               'filter-name'("ShiroFilter")
               'url-pattern'("/*")
               }
           }
       }
}

Этот код функционирует, когда инфрастуктура Grails исполняет файл web.xml.

Перед запуском прикладного подключаемого модуля на тестирование скопируйте в папку lib этого подключаемого модуля те JAR-файлы Shiro, которые вы ранее загрузили с помощью инструмента Ivy. После этого проверьте работоспособность подключаемого модуля с помощью следующей команды.

grails run-app

Если прикладной подключаемый модуль запускается успешно, вы сможете упаковать его для использования в учебном проекте. Для упаковки подключаемого модуля используйте следующую команду.

grails package-plugin

Установить новый модуль ShiroWebXmlPlugin можно посредством следующей команды.

cd myapp
grails install-plugin /path/to/shiro-web-xml-1.0.zip

Устранение неполадок

Если вы получили исключение UnavailableSecurityManagerException, это значит, что компонент SecurityManager не был настроен надлежащим образом. Убедитесь в том, что он настраивается на объекте SecurityUtils перед вызовом метода getSubject().

Соединение с LDAP-сервером может сопровождаться трудностями. Если вы получили исключение javax.naming.CommunicationException, проверьте имя хоста и порт LDAP-сервера. Если вы не используете Apache Directory, то продукт Apache Directory Studio (который можно установить отдельно) поможет вам устранить проблемы с соединением и с именами.

Если вы не использовали скрипт Ant Ivy для инициализации своей среды, то вы вполне можете получить ошибки, обусловленные отсутствием некоторых классов. Пример с INI-файлом исполняется даже без библиотеки Apache Commons Logging (commons-logging), однако исполнение примера с LDAP приводит к исключению. Используйте утилиту BeanUtils из состава Apache Commons для разбора INI-файла и для присвоения значений объектам.


Заключение

Shiro – это находящаяся на стадии Apache Incubator инфраструктура, позволяющая разработчику добавлять к своим приложениям функции аутентификации и авторизации. Shiro поддерживает разные хранилища аутентификационной информации, в том числе LDAP, Kerberos и AD DS. Среди преимуществ Shiro - минимальное количество зависимостей и относительная простота конфигурирования, что делает ее хорошим выбором для применения в качестве инфраструктуры безопасности вашего приложения.

Ресурсы

Научиться

Получить продукты и технологии

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Web-архитектура, SOA и web-сервисы
ArticleID=756796
ArticleTitle=Представляем Apache Shiro
publish-date=09092010