вторник, 29 ноября 2011 г.

Использование CAS-аутентификации в приложениях Grails

Применение CAS позволяет реализовать процедуру Single Sign On для web-приложений. Судя по всему, это протокол авторизации и аутентификации и его реализация от JASIG позволяет избегать многократных запросов логина и пароля у пользователя, что особенно удобно при переходе пользователя между разными web-приложениями.

Требования к установленному программному обеспечению
Перед началом настройки необходимо установить следующие программы (в скобках указаны версии, использованные мной):
  1. Свежая JDK, (JDK 7)
  2. Система сборки приложений MAVEN (3.0.3) для компиляции CAS-сервера
  3. Собственно сам Grails (1.3.7), отдельно я его не устанавливал, он был в составе соответствующего расширения для STS (2.8.1)
  4. Сервер БД MySQL (5.1.41-community) для хранения информации о пользователях и паролях
  5. JDBC-драйвер для подключения к БД MySQL (5.1.18)
Создание и настройка web-приложения в grails
Создание web-приложения grails выполняется как обычно командой: grails create-app TestCas
Но вообще все это проще делать в STS.
Далее следует установить плагин spring-security-core средствами STS или командой: grails install-plugin spring-security-core (у меня устанавливалась версия 1.2.4 плагина - последняя на данный момент).
Теперь надо создать классы пользователя и ролей web-приложения, это делается командой: grails s2-quickstart com.testapp User Role. При использовании STS, эту команду надо ввести в grails-консоли. Более подробную информацию об этом плагине можно посмотреть здесь.
Необходимо изменить файл web-приложения conf/BootStrap.groovy для того, чтобы заполнить базу данных исходными учетными данными. Файл должен выглядеть следующим образом:
import com.test.User


class BootStrap {
    def springSecurityService
    def init = { servletContext ->
        def user = new User(
            username: 'test', 
            password: springSecurityService.encodePassword('password'), 
            enabled: true, 
            accountExpired: false, 
            accountLocked: false, 
            passwordExpired: false)
  user.save(failOnError: true, flush: true)
    }


    def destroy = {
    }
}


Теперь настроим web-приложение на базу данных MySQL. Скопируем JDBC-драйвер (файл mysql-connector-java-5.1.18-bin.jar) в папку lib web-приложения. Меняем содержимое файла conf/DataSource.groovy таким образом:
dataSource {
pooled = true
driverClassName = "com.mysql.jdbc.Driver"
username = "[логин]"
password = "[пароль]"
}

hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}

environments {    development {
        dataSource {
            dbCreate = "create-drop"
            url = "jdbc:mysql://localhost/[имя базы данных]"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:file:prodDb;shutdown=true"
        }
    }
}

Для поддержки протокола CAS необходимо установить плагин spring-security-cas. Это делается командой: grails install-plugin spring-security-cas. Версия плагина на данный момент 1.0.2. У меня команда установки приводила к ошибкам разрешения зависимостей, поэтому мне так и не удалось установить плагин таким образом. Установка в ручном режиме описана ниже.
В файл conf/Config.groovy добавим строки настройки cas-клиента:
grails.plugins.springsecurity.cas.loginUri = '/login'
grails.plugins.springsecurity.cas.serviceUrl = 'http://localhost:8080/testcas/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.serverUrlPrefix = 'https://localhost:8443/cas'
grails.plugins.springsecurity.cas.proxyCallbackUrl = 'http://localhost:8080/testcas/'
grails.plugins.springsecurity.cas.proxyReceptorUrl = '/'

grails.plugins.springsecurity.cas.key = 'grails-spring-security-cas'
grails.plugins.springsecurity.cas.artifactParameter = 'ticket'
grails.plugins.springsecurity.cas.serviceParameter = 'service'
grails.plugins.springsecurity.cas.filterProcessesUrl = '/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.useSingleSignout = true


Описание настроек можно посмотреть здесь. Стоит обратить внимание, что "testcas" в значении настроек - это название нашего web-приложения, а настройка grails.plugins.springsecurity.cas.serverUrlPrefix содержит адрес CAS-сервера, который будет установлен в следующем разделе. Запустите web-приложение, если нет ошибок, то сделали все правильно и можно переходить к установке CAS-сервера.

Ручная установка плагина spring-security-cas

Сначала надо скачать плагин в виде zip-архива со страницы http://grails.org/plugin/spring-security-cas, там есть большая кнопка с надписью "Download".
Теперь, с помощью программы Far 2 (бесплатной на территории бывшего СССР), избавляем плагин от зависимостей. Открываем файл dependencies.groovy из архива grails-spring-security-cas-1.0.2.zip и комментируем строки между фигурными скобками раздела dependencies, т.е. выглядеть должно вот так:

grails.project.class.dir = 'target/classes'
grails.project.test.class.dir = 'target/test-classes'
grails.project.test.reports.dir = 'target/test-reports'
grails.project.docs.output.dir = 'docs'
grails.project.dependency.resolution = { 
    inherits('global') { 
        excludes 'commons-codec'
    } 
    log 'warn' repositories { 
        grailsPlugins()
        grailsHome()
        grailsCentral()
        ebr()
    }
    dependencies {
// compile( 'org.springframework.security:org.springframework.security.cas:3.0.4.RELEASE') { 
// transitive = false
// }
// compile( 'org.jasig.cas:com.springsource.org.jasig.cas.client:3.1.8') {
// transitive = false
// }
}
}
В папку lib web-приложения добавляем библиотеки:
Видимо, в версии 3.2.1 cas-клиента спецификация конструкторов изменилась, поэтому необходимо внести небольшое изменение в файл SpringSecurityCasGrailsPlugin.groovy в архиве плагина. Меняем строку:
casProxyRetriever(Cas20ProxyRetriever, conf.cas.serverUrlPrefix)
на строку:
casProxyRetriever(Cas20ProxyRetriever, conf.cas.serverUrlPrefix, "UTF-8")
После этого устанавливаем плагин из модифицированного архива командой: 
grails install-plugin file:///c:/temp/grails-spring-security-cas-1.0.2.zip
Создание контроллера для проверки работоспособности CAS-аутентификации
Создайте контроллер TestController с текстом:
import grails.plugins.springsecurity.Secured;

class TestController {
@Secured(['IS_AUTHENTICATED_REMEMBERED'])
    def index = { }
}
И представление:
<html>
<head>
<title>OK</title>
<meta name="layout" content="main" />
</head>

<body>
<h1>OK</h1>
</body>
</html>
Представление будет отображено в случае успеха аутентификации.


Установка CAS-сервера
CAS-сервер распространяется в исходных кодах, поэтому придется его компилировать. Качаем архив с исходниками со этой страницы. Распаковываем архив, заходи в его корень. запускаем процесс компиляции командой: mvn -DskipTests=true package install. Почему то тесты были выполнены не все - пришлось их отключить. Но это предварительная компиляция, мы ее выполнили, чтобы просто иметь под рукой необходимые компоненты, теперь настроим CAS-сервер и перекомпилируем его.
Добавим зависимости к проекту CAS-сервера, добавим приведенные ниже строки перед закрывающим тегом </dependencies> в файле pom.xml папки проекта cas-server-webapp:
<dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>cas-server-support-jdbc</artifactId>
    <version>${project.version}</version>
</dependency>


<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.18</version>
</dependency>


<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.4</version>
</dependency>
Настраиваем источник данных для получения информации об учетных записях, хранимой в БД MySQL. В файле src/main/webapp/WEB-INF/deployerConfigContext.xml перед закрывающим тегом </beans> добавим строки:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName">
        <value>com.mysql.jdbc.Driver</value>
    </property>
    <property name="url">
        <value>jdbc:mysql://localhost:3306/[имя базы данных]</value>
    </property>
    <property name="username"><value>[логин]</value></property>
    <property name="password"><value>[пароль]</value></property>
</bean>
Настройки настраиваемого источника данных должны совпадать с настройками web-приложения.
Перед закрывающим тегом </list> свойства authenticationHandlers необходимо добавить следующие строки:
<bean class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler">
    <property name="tableUsers"><value>user</value></property>
    <property name="fieldUser"><value>username</value></property>
    <property name="fieldPassword"><value>password</value></property>
    <property name="dataSource" ref="dataSource" />
    <property name="passwordEncoder">
        <bean 
        class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" 
        p:characterEncoding="UTF-8">
            <constructor-arg index="0" value="SHA-256" />
        </bean>
    </property>
</bean>
А предыдущую строку лучше закомментировать, судя по описанию, она представляет собой слишком простой обработчик аутентификации, за правильную аутентификацию принимая ситуацию идентичности логина и пароля.
В добавленных настройках определяется таблица (user) с информацией об учетных записях, а также поля логина (username), поле пароля (password) и используемый источник данных (dataSource). Свойство passwordEncoder необходимо для определения алгоритма шифрования паролей, grails по-умолчанию использует алгоритм построения хеша SHA-256, его наименование и указано в аргументе конструктора.
Далее переходим в корневую папку проекта cas-server-webapp и выполняем команду: mvn package install, если возникнут ошибки прохождения тестов, добавьте -DskipTest=true к командной строке.
В случае успешного выполнения компиляции, в папке target будет создан файл cas.war, его необходимо разместить на сервере приложений Tomcat или подобном, по умолчанию имя контекста приложения будет cas. Для обращения к нему по HTTP-протоколу используйте адрес http://localhost:8080/cas/, по протоколу HTTPS - https://localhost:8443/cas/. Если все настроено правильно вы можете ввести логин: test и пароль: password и будет отображена страница подтверждения правильности входа. Для выхода используйте URL  http://localhost:8080/cas//logout (https://localhost:8443/cas/logout).
Проверка установленных компонент
С помощью любого браузера заходим по адресу http://localhost:8080/testcas/test/, будет отображена страница авторизации CAS-сервера. Вводим правильные логин и пароль и видим страницу с надписью OK, вводим неправильные - на страницу с OK на не пускают. Поэкспериментируйте по разному с URL, приведенными в этом и предыдущем разделах, чтобы убедиться в корректности работы CAS-сервера и его связки с web-приложением.