Как интегрировать пользователя LDAP с таблицей PERSON, созданной Spring Security в Grails?

Мы создаем приложение grails, в котором мы хотим, чтобы пользователь входил в систему, используя свои учетные данные Active Directory. Кроме того, мы хотим дать бизнес-владельцу этого приложения возможность контролировать, кто имеет доступ к определенным ссылкам (действиям). По этой причине в нашем приложении grails мы используем следующие плагины:

  1. Ядро безопасности Spring
  2. Весенняя безопасность LDAP
  3. Пользовательский интерфейс Spring Security

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

На данный момент мы сделали следующее:

  • Мы можем успешно пройти аутентификацию в Active Directory.
  • Мы также смогли создать разные сопоставления запросов для разных ролей (ROLE_XXX) через интерфейс UI плагина spring-security-ui.

Проблемы / вопросы

Плагин spring-security-core создал следующие таблицы:

  • ЧЕЛОВЕК
  • ОРГАН ВЛАСТИ
  • PERSON_AUTHORITY
  • КАРТА ЗАПРОСА

Это таблицы, которые поддерживают создание ролей, назначение URL-адресов ролям. Однако таблица Person_Authoritity в качестве названия соглашения подразумевает, что между PERSON и AUTHORITY (ROLE) существует взаимосвязь «многие ко многим», поскольку человек потенциально может иметь более одной роли. Моя проблема в том, что у меня нет человека, потому что человек уже существует в Active Directory (внешний источник) и не был создан в приложении.

Есть ли способ сделать аутентифицированного пользователя ЛИЦОМ? Для решения безопасности Spring требуется эта строка или объект Person, однако вы предпочитаете ссылаться на него.

Я также разместил вопрос здесь:

http://grails.1312388.n4.nabble.com/Issues-integrating-LDAP-Authentication-with-Requestmap-to-Secure-URLs-td4644040.html

Спасибо,


person Viriato    schedule 29.04.2013    source источник
comment
Вот что мы сделали. У нас была аналогичная ситуация, как эта, когда пользователь аутентифицировался по LDAP, что мы в итоге сделали, так это то, что как только человек аутентифицирован по LDAP, мы использовали его для поиска в таблице лиц, и если он не существует, мы бы создайте его там с некоторой базовой информацией, а затем добавьте этот объект в RequestMap. Если этот человек уже существует в таблице «Персоны», мы не добавляем его, но ключевым моментом было то, что в обоих случаях пользователь все еще аутентифицируется по LDAP.   -  person allthenutsandbolts    schedule 30.04.2013
comment
@allthenutsandbolts Пробовал безуспешно. Поле пароля является обязательным полем, как передать поле j_password действию пользователя / сохранения, которое создает человека? При отправке логина в j_security_check у меня больше нет доступа к учетным данным для вставки записи о человеке. Я попытался создать пользователя в таблице людей вручную через Spring-security-ui, все еще проходя аутентификацию через LDAP, но когда я перешел на защищенный URL-адрес, это не сработало. Похоже, есть разрыв между пользователем в таблице людей и пользователь в LDAP, а может что-то не зарегистрировано или не инициализировано.   -  person Viriato    schedule 01.05.2013
comment
Вы можете установить этот пароль на все, что захотите. Вы не будете использовать его для аутентификации. Вы правы, у вас нет доступа к полю j_password.   -  person allthenutsandbolts    schedule 01.05.2013
comment
Да, я понимаю, но давайте представим этот сценарий: скажем, что мой пользователь LDAP называется test, я захожу в создание записи с тестовым именем пользователя в таблице людей через spring -security-ui с ролью ROLE_USER, дополнительно я защищаю URL-адрес (admin / addCourse) в роль ROLE_ADMIN. Когда я вхожу в систему под тестом, если я перехожу к URL-адресу admin / addCourse , это позволяет мне. Таким образом, кажется, что нужно вызвать что-то, что связывает тестового пользователя LDAP с записью тестового человека, чтобы предотвратить доступ тестового пользователя с ролью ROLE_USER к защищенному URL-адресу, доступному только под ROLE_ADMIN. Ты знаешь, что я имею в виду?   -  person Viriato    schedule 01.05.2013


Ответы (1)


Таким образом, вам необходимо сопоставить пользователя AD с человеком.

Вот 3 класса, которые вам нужны в src / groovy. Очевидно, измените их по мере необходимости:

    package yourpackagename

    import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser
    import org.springframework.security.core.GrantedAuthority

    class CustomUserDetails extends GrailsUser{
        final String firstName
        final String lastName

        CustomUserDetails(String username, String password, boolean enabled,
                          boolean accountNonExpired, boolean credentialsNonExpired,
                          boolean accountNonLocked,
                          Collection<GrantedAuthority> authorities,
                          long id, String firstName, String lastName) {
            super(username, password, enabled, accountNonExpired,
                    credentialsNonExpired, accountNonLocked, authorities, id)

            this.firstName = firstName
            this.lastName = lastName
        }
    }

package yourpackagenamehere

import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserDetailsService
import org.springframework.security.core.authority.GrantedAuthorityImpl
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

class CustomUserDetailsService implements GrailsUserDetailsService {

    /**
     * Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least one role, so
     * we give a user with no granted roles this one which gets past that restriction but
     * doesn't grant anything.
     */
    static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]

    UserDetails loadUserByUsername(String username, boolean loadRoles)
    throws UsernameNotFoundException {
        return loadUserByUsername(username)
    }

    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User.withTransaction { status ->

            User user = User.findByUsername(username)
            if (!user) throw new UsernameNotFoundException('User not found', username)

            def authorities = user.authorities.collect {new GrantedAuthorityImpl(it.authority)}

            return new CustomUserDetails(user.username, user.password, user.enabled,
                    !user.accountExpired, !user.passwordExpired,
                    !user.accountLocked, authorities ?: NO_ROLES, user.id,
                    user.firstName, user.lastName)
        } as UserDetails
    }
}

package yourpackagenamehere

import groovy.sql.Sql

import org.springframework.ldap.core.DirContextAdapter
import org.springframework.ldap.core.DirContextOperations
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper
import org.springframework.security.core.authority.GrantedAuthorityImpl
import org.springframework.security.core.GrantedAuthority
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.authentication.DisabledException

class CustomUserDetailsContextMapper implements UserDetailsContextMapper {

    private static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]

    def dataSource

    @Override
    public CustomUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<GrantedAuthority> authority) {

        username = username.toLowerCase()

        User user = User.findByUsername(username)

        String firstName = ctx.originalAttrs.attrs['givenname'].values[0]
        String lastName = ctx.originalAttrs.attrs['sn'].values[0]


        def roles

        User.withTransaction {

            if(!user){
                user = new User(username: username, enabled: true, firstName: firstName, lastName: lastName)
                user.save(flush: true)
            }
            else {
                user = User.findByUsername(username)
                user.firstName = firstName
                user.lastName = lastName
                user.save(flush: true)
            }

            roles = user.getAuthorities()
        }

        if ( !user.enabled )
            throw new DisabledException("User is disabled", username)


        def authorities = roles.collect { new GrantedAuthorityImpl(it.authority) }
        authorities.addAll(authority)
        def userDetails = new CustomUserDetails(username, user.password, user.enabled, false, false, false, authorities, user.id, user.firstName, user.lastName)

        return userDetails
    }

    @Override
    public void mapUserToContext(UserDetails arg0, DirContextAdapter arg1) {
    }
}

В конфигурации в spring / resources.groovy:

import yourpackagenamehere.CustomUserDetailsService
import yourpackagenamehere.CustomUserDetailsContextMapper
beans = {
    userDetailsService(CustomUserDetailsService)

    ldapUserDetailsMapper(CustomUserDetailsContextMapper) {
        dataSource = ref("dataSource")
    }
}

Вот мои настройки в Config.groovy:

grails.plugins.springsecurity.ldap.context.managerDn = 'CN=username,OU=People,DC=foo,DC=com'
grails.plugins.springsecurity.ldap.context.managerPassword = 'password'
grails.plugins.springsecurity.ldap.context.server = 'ldap://foo.com:389/'
grails.plugins.springsecurity.ldap.authorities.ignorePartialResultException = true
grails.plugins.springsecurity.ldap.search.base = 'ou=People,dc=foo,dc=com'
grails.plugins.springsecurity.ldap.search.filter="sAMAccountName={0}"
grails.plugins.springsecurity.ldap.search.searchSubtree = true
grails.plugins.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
grails.plugins.springsecurity.ldap.search.attributesToReturn = null
grails.plugins.springsecurity.providerNames = ['ldapAuthProvider', 'anonymousAuthenticationProvider']
grails.plugins.springsecurity.ldap.mapper.userDetailsClass = 'CustomUserDetails'

grails.plugins.springsecurity.ldap.authorities.retrieveGroupRoles = true
grails.plugins.springsecurity.ldap.authorities.retrieveDatabaseRoles = true
grails.plugins.springsecurity.ldap.authorities.groupSearchBase ='dc=foo,dc=com'
grails.plugins.springsecurity.ldap.authorities.groupSearchFilter = 'member={0}' 
person James Kleeh    schedule 29.04.2013
comment
Спасибо за ваш ответ. Я постараюсь заставить его работать по вашему предложению и дам вам знать. - person Viriato; 01.05.2013
comment
спасибо большое, сработало !!! После прочтения документации grails -plugins.github.io/grails-spring-security-ldap/docs/ мне не было очевидно, что переопределение этих двух классов поможет. Мне также пришлось сделать пароль допускающим значение NULL: true и blank: true в классе домена, а также убедиться, что он допускает значение NULL «Y» на стороне базы данных. У меня проблема с тем, что объект DirContextOperations всегда имеет значение null, когда я запускаю приложение. Не уверен, что это такое, но пока я могу жить с этим. - person Viriato; 03.05.2013
comment
Знаете что, у меня раньше была эта ошибка! Он не удался, когда работал в AIX, но работал в Windows. Две машины находятся в разных подсетях, и я списал это на сетевую штуку. Пожалуйста, дайте мне знать, если разберетесь !!!!!!!! - person James Kleeh; 03.05.2013