Grails openid 插件 2.0.0 RC1:得到“没有这样的属性:用户异常”
Posted
技术标签:
【中文标题】Grails openid 插件 2.0.0 RC1:得到“没有这样的属性:用户异常”【英文标题】:Grails openid plugin 2.0.0 RC1: getting "No such property: User exception" 【发布时间】:2013-11-06 15:24:55 【问题描述】:已将openid plugin 2.0.0 RC1 安装到我的 grails 应用程序中,但我得到了 没有这样的属性:用户异常。有人可以建议应该做什么吗?
| Error 2013-10-28 00:11:55,169 [http-bio-8080-exec-6] ERROR errors.GrailsExceptionResolver - MissingPropertyException occurred when processing request: [POST] /Test/login/openIdCreateAccount - parameters:
username: valid_gmail@gmail.com
password2: ***
password: ***
No such property: User for class: grails.plugin.springsecurity.openid.OpenIdRegisterCommand. Stacktrace follows:
Message: No such property: User for class: grails.plugin.springsecurity.openid.OpenIdRegisterCommand
Line | Method
->> 265 | doCall in grails.plugin.springsecurity.openid.OpenIdRegisterCommand$__clinit__closure1_closure2
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 195 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter
| 53 | doFilter in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
| 49 | doFilter in grails.plugin.springsecurity.web.authentication.RequestHolderAuthenticationFilter
| 82 | doFilter in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
| 895 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker
| 918 | run in ''
^ 695 | run . . in java.lang.Thread
【问题讨论】:
您的应用中是否安装了Spring Security Core plugin?如果答案是否定的,那么RTFM。 嗨,是的,它已安装。当我在 gmail 身份验证后被重定向并尝试为给定的 openid 注册新用户时,我遇到了异常。 【参考方案1】:这个bug在RC2中没有修复,我两天前安装了它,遇到了同样的问题。实际上,OpenIdController 中有 4 个错误。我将在下面的建议答案中提到这些错误的详细信息以及 user2576874 修复的解决方案:
1:第 267 行,类引用了 OpenIdRegistrationCommand 的静态闭包中的 grailsApplication bean。这里给出的解决方案是将grailsApplication
替换为:grails.util.Holders.getGrailsApplication()
2:第 286 行,当我们在 OpenId 登录回调后尝试注册用户时,regEx 失败。这是一个更简单的正则表达式条件:
(!password.matches('^.*[a-zA-Z].*$') ||
!password.matches('^.*[0-9].*$') ||
!password.matches('^.*[!@#$%^&].*$')))
3:第 183 行,字段名称有错误:usernamePropertyName
。正确的字段名称是usernamePropName
,如第 49 行所述。
4:运行代码会抛出 NullPointerException,因为 Role 类未定义类型。解决方案是将类类型添加到与 User 和 UserRole 定义相同的变量 Role 中。在 OpenIdConroller 的第 255 行之后添加了以下代码:
Role = grailsApplication.getClassForName(conf.authority.className)
谢谢
【讨论】:
【参考方案2】:这是对我有用的版本。您能否在此基础上从插件中发布一个新的 RC3?
/* Copyright 2013 SpringSource.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package grails.plugin.springsecurity.openid
import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.annotation.Secured
import org.springframework.beans.factory.InitializingBean
import org.springframework.orm.hibernate3.SpringSessionContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.AuthenticationException
/**
* Manages associating OpenIDs with application users, both by creating a new local user
* associated with an OpenID and also by associating a new OpenID to an existing account.
*/
@Secured('permitAll')
class OpenIdController implements InitializingBean
/** Dependency injection for daoAuthenticationProvider. */
def daoAuthenticationProvider
/** Dependency injection for the grailsApplication. */
def grailsApplication
/** Dependency injection for OpenIDAuthenticationFilter. */
def openIDAuthenticationFilter
/** Dependency injection for the requestCache. */
def requestCache
/** Dependency injection for the springSecurityService. */
def springSecurityService
private Class User
private Class UserRole
private Class Role
private String usernamePropName
private String passwordPropertyName
private String enabledPropertyName
private String roleNameField
static defaultAction = 'auth'
static scope = 'singleton'
/**
* Shows the login page. The user has the choice between using an OpenID and a username
* and password for a local account. If an OpenID authentication is successful but there
* is no corresponding local account, they'll be redirected to createAccount to create
* a new account, or click through to linkAccount to associate the OpenID with an
* existing local account.
*/
def auth()
def config = SpringSecurityUtils.securityConfig
if (springSecurityService.isLoggedIn())
redirect uri: config.successHandler.defaultTargetUrl
return
[openIdPostUrl: "$request.contextPath$openIDAuthenticationFilter.filterProcessesUrl",
daoPostUrl: "$request.contextPath$config.apf.filterProcessesUrl",
persistentRememberMe: config.rememberMe.persistent,
rememberMeParameter: config.rememberMe.parameter,
openidIdentifier: config.openid.claimedIdentityFieldName]
/**
* Initially we're redirected here after a UserNotFoundException with a valid OpenID
* authentication. This action is specified by the openid.registration.createAccountUri
* attribute.
* <p/>
* The GSP displays the OpenID that was received by the external provider and keeps it
* in the session rather than passing it between submits so the user has no opportunity
* to change it.
*/
def createAccount(OpenIdRegisterCommand command)
String openId = session[OpenIdAuthenticationFailureHandler.LAST_OPENID_USERNAME]
if (!openId)
flash.error = 'Sorry, an OpenID was not found'
return [command: command]
if (!request.post)
// show the form
command.clearErrors()
copyFromAttributeExchange command
return [command: command, openId: openId]
if (command.hasErrors())
return [command: command, openId: openId]
if (!createNewAccount(command.username, command.password, openId))
return [command: command, openId: openId]
authenticateAndRedirect command.username
/**
* The registration page has a link to this action so an existing user who successfully
* authenticated with an OpenID can associate it with their account for future logins.
*/
def linkAccount(OpenIdLinkAccountCommand command)
String openId = session[OpenIdAuthenticationFailureHandler.LAST_OPENID_USERNAME]
if (!openId)
flash.error = 'Sorry, an OpenID was not found'
return [command: command]
if (!request.post)
// show the form
command.clearErrors()
return [command: command, openId: openId]
if (command.hasErrors())
return [command: command, openId: openId]
try
registerAccountOpenId command.username, command.password, openId
catch (AuthenticationException e)
flash.error = 'Sorry, no user was found with that username and password'
return [command: command, openId: openId]
authenticateAndRedirect command.username
/**
* Authenticate the user for real now that the account exists/is linked and redirect
* to the originally-requested uri if there's a SavedRequest.
*
* @param username the user's login name
*/
protected void authenticateAndRedirect(String username)
session.removeAttribute OpenIdAuthenticationFailureHandler.LAST_OPENID_USERNAME
session.removeAttribute OpenIdAuthenticationFailureHandler.LAST_OPENID_ATTRIBUTES
springSecurityService.reauthenticate username
def config = SpringSecurityUtils.securityConfig
def savedRequest = requestCache.getRequest(request, response)
if (savedRequest && !config.successHandler.alwaysUseDefault)
redirect url: savedRequest.redirectUrl
else
redirect uri: config.successHandler.defaultTargetUrl
/**
* Create the user instance and grant any roles that are specified in the config
* for new users.
* @param username the username
* @param password the password
* @param openId the associated OpenID
* @return true if successful
*/
protected boolean createNewAccount(String username, String password, String openId)
boolean created = User.withTransaction status ->
def config = SpringSecurityUtils.securityConfig
password = encodePassword(password)
def user = User.newInstance((usernamePropName): username,
(passwordPropertyName): password,
(enabledPropertyName): true)
user.addToOpenIds(url: openId)
if (!user.save())
return false
for (roleName in config.openid.registration.roleNames)
UserRole.create user, Role.findWhere((roleNameField): roleName)
return true
return created
protected String encodePassword(String password)
def config = SpringSecurityUtils.securityConfig
def encode = config.openid.encodePassword
if (!(encode instanceof Boolean)) encode = false
if (encode)
password = springSecurityService.encodePassword(password)
password
/**
* Associates an OpenID with an existing account. Needs the user's password to ensure
* that the user owns that account, and authenticates to verify before linking.
* @param username the username
* @param password the password
* @param openId the associated OpenID
*/
protected void registerAccountOpenId(String username, String password, String openId)
// check that the user exists, password is valid, etc. - doesn't actually log in or log out,
// just checks that user exists, password is valid, account not locked, etc.
daoAuthenticationProvider.authenticate new UsernamePasswordAuthenticationToken(username, password)
User.withTransaction status ->
def user = User.findWhere((usernamePropName): username)
user.addToOpenIds(url: openId)
if (!user.validate())
status.setRollbackOnly()
/**
* For the initial form display, copy any registered AX values into the command.
* @param command the command
*/
protected void copyFromAttributeExchange(OpenIdRegisterCommand command)
List attributes = session[OpenIdAuthenticationFailureHandler.LAST_OPENID_ATTRIBUTES] ?: []
for (attribute in attributes)
// TODO document
String name = attribute.name
if (command.hasProperty(name))
command."$name" = attribute.values[0]
void afterPropertiesSet() throws Exception
def conf = SpringSecurityUtils.securityConfig
usernamePropName = conf.userLookup.usernamePropertyName
passwordPropertyName = conf.userLookup.passwordPropertyName
enabledPropertyName = conf.userLookup.enabledPropertyName
roleNameField = conf.authority.nameField
User = grailsApplication.getClassForName(conf.userLookup.userDomainClassName)
UserRole = grailsApplication.getClassForName(conf.userLookup.authorityJoinClassName)
Role = grailsApplication.getClassForName(conf.authority.className)
class OpenIdRegisterCommand
String username = ""
String password = ""
String password2 = ""
static constraints =
username blank: false, validator: String username, command ->
def User = grails.util.Holders.getGrailsApplication().getClassForName(SpringSecurityUtils.securityConfig.userLookup.userDomainClassName)
User.withNewSession session ->
if (username)
boolean exists = User.createCriteria().count
eq SpringSecurityUtils.securityConfig.userLookup.usernamePropertyName, username
if (exists)
return 'openIdRegisterCommand.username.error.unique'
password blank: false, minSize: 8, maxSize: 64, validator: password, command ->
if (command.username && command.username.equals(password))
return 'openIdRegisterCommand.password.error.username'
if (password && password.length() >= 8 && password.length() <= 64 &&
(!password.matches('^.*[a-zA-Z].*$') ||
!password.matches('^.*[0-9].*$') ||
!password.matches('^.*[!@#$%^&].*$'))
)
return 'openIdRegisterCommand.password.error.strength'
password2 validator: password2, command ->
if (command.password != password2)
return 'openIdRegisterCommand.password2.error.mismatch'
class OpenIdLinkAccountCommand
String username = ""
String password = ""
static constraints =
username blank: false
password blank: false
【讨论】:
【参考方案3】:这是一个错误,我修复了它并发布了 2.0-RC2。请更新到该版本,看看它是否有效。
【讨论】:
嗨,我更新了,但现在 grailsApplication 出现同样的错误。看起来 OpenIdRegisterCommand 在闭包中没有看到来自父类的 grailsApplication 在这一行:def User = grailsApplication.getClassForName(SpringSecurityUtils.securityConfig.userLookup.userDomainClassName) 您好,能否请您发布一个新版本 RC3,并提供解决方案?以上是关于Grails openid 插件 2.0.0 RC1:得到“没有这样的属性:用户异常”的主要内容,如果未能解决你的问题,请参考以下文章
Grails 2.0.0.RC3:无法重新加载对控制器的更改,java.lang.NoSuchFieldException:__timeStamp__239_neverHappen1323622798
grails 2.4.0.M1 中的 spring 安全插件出错