grails - Grails 中的错误 : Save the transient instance before flushing

标签 grails groovy spring-security spring-security-ldap

我在使用 spring-security 和 LDAP 在 Grails 应用程序中运行用户身份验证时遇到问题。

与 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/groovvy/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/CustomUserDetailsS​​ervice.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

最佳答案

我也遇到了同样的问题。错误消息指出用户实例未保存。我通过更改 CustomUserDetailsMapper.grovvy 中的以下行来修复它

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

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

并向 User 域类添加firstName 和lastName。

正如您所看到的,我刚刚向应该创建的用户添加了一些默认值。密码始终设置为“test”并不重要。由于您正在使用 LDAP,因此不会使用它。

关于grails - Grails 中的错误 : Save the transient instance before flushing,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20288478/

相关文章:

Grails Spring Security 插件 - 登录表单重定向到 ajaxAuth

bash - 从 groovy 运行外部进程

groovy - 如何在 Jenkins Workflow 中包含外部代码文件

java - Spring 安全。不同角色对同一页面的不同 View ,可能吗?

java - 与 .Net 共享 Java HTTP session

grails - Grails域验证器

xml - 如何使用Groovy构建器StreamingMarkupBuilder在UTF-8中转换XML文件

grails - 遍历 Grails Controller 模型

grails - 为什么Class.forName在Grails域类上不起作用

java - 基本身份验证后 Spring Security 抛出 403