Ошибка в Grails: сохраните временный экземпляр перед очисткой

У меня возникла проблема с запуском аутентификации пользователя в приложении Grails с Spring-Security и LDAP.

Соединение с LDAP работает нормально, я получаю результаты. Но мне не удавалось, чтобы пользователь мог войти в систему и чтобы данные сохранялись в локальной базе данных.

Я изменил/создал следующие файлы:

config.groovy

grails.plugin.springsecurity.ldap. context.managerDn = 'USERNAME'
grails.plugin.springsecurity.ldap. context.managerPassword = 'PASSWORD'
grails.plugin.springsecurity.ldap. context.server ='ldap://LDAPSERVER:389/'
grails.plugin.springsecurity.ldap. authorities.ignorePartialResultException = true // typically needed for Active Directory
grails.plugin.springsecurity.ldap. search.base = 'DC=example,DC=com'
grails.plugin.springsecurity.ldap. search.filter='(sAMAccountName={0})' // for Active Directory you need this
grails.plugin.springsecurity.ldap. search.searchSubtree = true
grails.plugin.springsecurity.ldap.authorities.groupSearchBase ='DC=example,DC=com'
grails.plugin.springsecurity.ldap.authorities.groupSearchFilter = 'member={0}'
grails.plugin.springsecurity.ldap.authorities.retrieveDatabaseRoles = false
grails.plugin.springsecurity.ldap. auth.hideUserNotFoundExceptions = false
grails.plugin.springsecurity.ldap. search.attributesToReturn = ['mail', 'displayName', 'title', 'fullname'] // extra attributes you want returned; see below for custom classes that access this data
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider']
grails.plugin.springsecurity.ldap.useRememberMe = false
grails.plugin.springsecurity.ldap.authorities.defaultRole = 'ROLE_USER'
grails.plugin.springsecurity.ldap.mapper.userDetailsClass = 'CustomUserDetails'

src/grovvy/CustomUserDetailsMapper.grovvy

package com.example


import com.example.CustomUserDetails
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 groovy.sql.Sql

import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.GrantedAuthority


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

class CustomUserDetailsContextMapper implements UserDetailsContextMapper {

    private static final List NO_ROLES = [new SimpleGrantedAuthority("ROLE_USER")]

    def dataSource

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

        username = username.toLowerCase()

        User.withTransaction {

        User user = User.findByUsername(username)

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


        def roles



            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)
            }

            roles = user.getAuthorities()
        }

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


        def authorities = roles.collect { new SimpleGrantedAuthority(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) {
    }
}

src/grovvy/CustomUserDetails.groovy

 package com.example


 import org.springframework.security.core.GrantedAuthority
 import org.springframework.security.core.userdetails.User


 class CustomUserDetails extends User{
         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
         }
     }

src/groovy/CustomUserDetailsService.groovy

package com.example


import grails.plugin.springsecurity.userdetails.GrailsUserDetailsService
import  org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException


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 SimpleGrantedAuthority("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 SimpleGrantedAuthority(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
   }
 }

conf/resources.groovy

// Place your Spring DSL code here
import com.example.CustomUserDetailsContextMapper
import com.example.CustomUserDetailsService

beans = {
  userDetailsService(CustomUserDetailsService)

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

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

 Message: object references an unsaved transient instance - save the transient instance before flushing: com.example.User; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.example.User

person mbs    schedule 29.11.2013    source источник


Ответы (2)


У меня такая же проблема. В сообщении об ошибке говорится, что экземпляр пользователя не был сохранен. Я исправил это, изменив следующую строку в CustomUserDetailsMapper.grovvy

user = new User(username: username, enabled: true, firstName: firstName, lastName: lastName)

to

user = new User(username: username, enabled: true, firstName: firstName, lastName: lastName, accountLocked: false, passwordExpired: false, accountExpired: false, password: "test")

и добавив имя и фамилию в класс домена пользователя.

Как видите, я только что добавил некоторые значения по умолчанию для пользователя, который должен быть создан. Неважно, что пароль всегда установлен на «тест». Он не будет использоваться, поскольку вы используете LDAP.

person Zoidberg    schedule 02.12.2013

Для тех, кто вместо этого изменил сгенерированный пользовательский класс из весенней безопасности (после запуска сценария быстрого запуска) и получил ту же ошибку, я добавил nullable: true в static constraints в пользовательском классе домена ко всем вашим пользовательским свойствам - в этом случае firstName и фамилия. Это позволяет сохранить экземпляр без явной установки всех свойств.

Надеюсь, это поможет кому-то!

static constraints = { username blank: false, unique: true password blank: false fname nullable: true lname nullable: true }

person Pudpuduk    schedule 25.04.2014