为啥我无法使用 grails 框架 1.3.7 中安装的 spring security 和 weceem 插件登录

Posted

技术标签:

【中文标题】为啥我无法使用 grails 框架 1.3.7 中安装的 spring security 和 weceem 插件登录【英文标题】:Why can't i login with spring security and weceem plugin installed in grails framework 1.3.7为什么我无法使用 grails 框架 1.3.7 中安装的 spring security 和 weceem 插件登录 【发布时间】:2011-09-14 13:01:23 【问题描述】:

我正在开发一个几乎干净的 grails 1.3.7 项目,其中安装了 weceem 1.0RC2、spring-security-core 1.1.3、spring-security-ui 0.1.2、weceem-spring-security 1.0 及其依赖项。

除了用户登录,一切正常。当我想通过http://localhost:8080/appname/login 登录时,我只收到以下错误消息:

Sorry, we were not able to find a user with that username and password.

但是用户仍然存在于数据库中,如果我使用由 spring-security-ui 创建的用户,我会收到相同的错误消息。要对密码进行编码,我使用的是 springSecurityService.encodePassword('password')。 LoginController 由 spring-security (s2-quickstart) 生成。

我认为 weceem - spring-security 桥可能有问题,你有什么意见?

最好的问候, whitenexx

import grails.converters.JSON
import javax.servlet.http.HttpServletResponse
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
import org.springframework.security.authentication.AccountExpiredException
import org.springframework.security.authentication.CredentialsExpiredException
import org.springframework.security.authentication.DisabledException
import org.springframework.security.authentication.LockedException
import org.springframework.security.core.context.SecurityContextHolder as SCH
import org.springframework.security.web.WebAttributes
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

class LoginController 

/**
 * Dependency injection for the authenticationTrustResolver.
 */
def authenticationTrustResolver

/**
 * Dependency injection for the springSecurityService.
 */
def springSecurityService

/**
 * Default action; redirects to 'defaultTargetUrl' if logged in, /login/auth otherwise.
 */
def index = 
    if (springSecurityService.isLoggedIn()) 
        redirect uri: SpringSecurityUtils.securityConfig.successHandler.defaultTargetUrl
    
    else 
        redirect action: auth, params: params
    


/**
 * Show the login page.
 */
def auth = 

    def config = SpringSecurityUtils.securityConfig

    if (springSecurityService.isLoggedIn()) 
        redirect uri: config.successHandler.defaultTargetUrl
        return
    

    String view = 'auth'
    String postUrl = "$request.contextPath$config.apf.filterProcessesUrl"
    render view: view, model: [postUrl: postUrl,
                rememberMeParameter: config.rememberMe.parameter]


/**
 * The redirect action for Ajax requests. 
 */
def authAjax = 
    response.setHeader 'Location', SpringSecurityUtils.securityConfig.auth.ajaxLoginFormUrl
    response.sendError HttpServletResponse.SC_UNAUTHORIZED


/**
 * Show denied page.
 */
def denied = 
    if (springSecurityService.isLoggedIn() &&
    authenticationTrustResolver.isRememberMe(SCH.context?.authentication)) 
        // have cookie but the page is guarded with IS_AUTHENTICATED_FULLY
        redirect action: full, params: params
    


/**
 * Login page for users with a remember-me cookie but accessing a IS_AUTHENTICATED_FULLY page.
 */
def full = 
    def config = SpringSecurityUtils.securityConfig
    render view: 'auth', params: params,
            model: [hasCookie: authenticationTrustResolver.isRememberMe(SCH.context?.authentication),
                postUrl: "$request.contextPath$config.apf.filterProcessesUrl"]


/**
 * Callback after a failed login. Redirects to the auth page with a warning message.
 */
def authfail = 

    def username = session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
    String msg = ''
    def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
    if (exception) 
        if (exception instanceof AccountExpiredException) 
            msg = SpringSecurityUtils.securityConfig.errors.login.expired
        
        else if (exception instanceof CredentialsExpiredException) 
            msg = SpringSecurityUtils.securityConfig.errors.login.passwordExpired
        
        else if (exception instanceof DisabledException) 
            msg = SpringSecurityUtils.securityConfig.errors.login.disabled
        
        else if (exception instanceof LockedException) 
            msg = SpringSecurityUtils.securityConfig.errors.login.locked
        
        else 
            msg = SpringSecurityUtils.securityConfig.errors.login.fail
        
    

    if (springSecurityService.isAjax(request)) 
        render([error: msg] as JSON)
    
    else 
        flash.message = msg
        redirect action: auth, params: params
    


/**
 * The Ajax success redirect url.
 */
def ajaxSuccess = 
    render([success: true, username: springSecurityService.authentication.name] as JSON)


/**
 * The Ajax denied redirect url.
 */
def ajaxDenied = 
    render([error: 'access denied'] as JSON)


【问题讨论】:

我记录了 mysql 查询,发现:选择 this_.id 作为 id9_0_,this_.version 作为 version9_0_,this_.account_expired 作为 account3_9_0_,this_.account_locked 作为 account4_9_0_,this_.enabled 作为 enabled9_0_,this_。 password as password6_9_0_, this_.password_expired as password7_9_0_, this_.username as username9_0_ from user this_ where this_.username='admin'----->我认为有错误? 【参考方案1】:

我刚刚解决了一个症状相同的问题。

原来我在 Config.groovy 中的映射闭包有错字,我将一个不存在的字段映射到用户 weceem 视图中的“密码”字段。

所以插件注入的自定义 UserDetailsS​​ervice 只是讨厌我的用户对象,没有任何工作正常。

我在映射的域端将 passwd 更改为密码,以使其与我的用户对象中的实际内容相匹配,并且一切正常。

【讨论】:

【参考方案2】:

从您提供的少量信息来看,这有点棘手。 Weceem Spring Security 插件将 Spring Security Core 连接到 Weceem 的身份验证机制。

它通过提供一个自定义的 UserDetailsS​​ervice 实现来实现这一点,该实现从域类映射到 Spring Security Core 使用的会话数据对象。

这个登录 URL,是否映射到上面详述的您自己的登录控制器? weceem-spring-security插件中的UserDetailsS​​ervice使用配置的用户域类调用findByUsername(username):

void afterPropertiesSet() 
    def conf = grailsApplication.config
    def clsname = conf.grails.plugins.springsecurity.userLookup.userDomainClassName
    domainClass = grailsApplication.getDomainClass(clsname).clazz

    def mapper = conf.weceem.springsecurity.details.mapper
    if (!(mapper instanceof Closure)) 
        throw new IllegalArgumentException(
            "Your Config must specify a closure in weceem.springsecurity.details.mapper "+
            "that maps the domain model to a non-domain object, providing at least: $REQUIRED_MAPPED_FIELDS")
    
    detailsMapper = mapper


UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 

    domainClass.withTransaction  status ->

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

...

所以正如你从上面看到的,我认为最后一行可能是因为一些 spring 域类/用户名问题而放弃了你的地方?

如果问题与安装后登录到 Weceem 相关(看起来并非如此),您需要确保您已配置 Weceem Spring Security 应如何从您的用户域类映射到 weceem 所需的内部数据和 spring sec 核心功能,请参阅:

http://grails.org/plugin/weceem-spring-security

【讨论】:

阅读上面的@Dan解决方案,我可以建议您更改文档,以便映射闭包中的字段名是密码:密码,而不是密码:密码-谢谢:) 也许有人可以通过一些简短的步骤告诉我如何使用 spring security、weceem 和 spring security weceem 插件设置一个 grails 1.3.7 应用程序?我想我的错误在别处……谢谢! :)

以上是关于为啥我无法使用 grails 框架 1.3.7 中安装的 spring security 和 weceem 插件登录的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用 JBoss 进行 Grails 部署?

Grails 1.3.7 / Java 7 兼容性

如何在 grails 1.3.7 中记录 sql

为啥 Grails 使用 Ivy 作为构建和依赖管理器? [关闭]

将 Grails 应用程序从 1.3.7 升级到 Java 8 兼容版本

Grails 1.3.7 - build.xml 在哪里?