Grails spring-security - 我可以在成功操作之前拦截,以检查所需的密码更改吗? [英] Grails spring-security - Can I intercept before success action to check required password change?

查看:114
本文介绍了Grails spring-security - 我可以在成功操作之前拦截,以检查所需的密码更改吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的系统中创建新用户时,我通过电子邮件向他们发送临时密码并设置changePasswordNextLogin = true的属性。当他们第一次登录时,我想在成功登录时拦截流量,检查此值,如果为真,则将其重定向到更改密码操作。一旦密码更改完成,理想情况下,我想将它们发送到预定的目的地。

我一直在倾注默认设置,并且没有看到 - 或者更可能没有正确解释 - 以任何方式实现此目的。似乎几乎每次我在Grails中尝试拼凑一些解决方案时,我发现有人已经做出了更加优雅的方法来做同样的事情。是否有任何内置的功能可以实现这个功能?



如果没有,我真的很感激任何关于最佳方法的建议。 方案

直接使用Spring Security和grails插件有一些支持,但你也必须自己做一些工作:)



在安装grails-spring-security插件(并运行S2Quickstart脚本)时创建的域类有一个名为'passwordExpired'的属性。当您创建新的用户域实例时,将其设置为true。



一旦该用户第一次登录,Spring Security核心库将引发一个异常,您可以赶上你的登录控制器的authfail关闭,重新引导他们到变更密码表单(你需要提供自己的)。



下面是我的一个应用程序的例子,此封闭的骨架版本应该已经包含在您的登录控制器中:

  / ** 
*登录失败。
* /
def authfail = {

def msg =''

def username =
session [UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
$ b $ def exception = session [WebAttributes.AUTHENTICATION_EXCEPTION]

if(exception){
if(异常instanceof CredentialsExpiredException){
msg = g.message(code :springSecurity.errors.login.passwordExpired)
if(!springSecurityService.isAjax(request))
redirect(action:'changePassword')//< - 见下面
}
//其他失败检查省略

$ b $ if(springSecurityService.isAjax(request)){
render([error:msg] as JSON)
}
else {
flash.message = msg
重定向控制器:'login',action:'auth',params:params
}
}

/ **
*呈现更改密码形式
* /
def changeP assword = {
[username:session [UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]?:springSecurityService.authentication.name]
}



从您的'changePasssword'视图中,将表单提交回另一个控制器闭包(我称之为'updatePassword',它检查您想要的任何密码约束并将更新后的密码保存在域对象上..

  def updatePassword = {
字符串用户名=会话[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]?:springSecurityService.authentication.name
if(!username){
flash.message ='抱歉,发生了错误'
重定向控制器:'login',操作:'auth'
返回
}
字符串密码= params.password
字符串newPassword = params.password_new
字符串newPassword2 = params.password_new_2
if(!passwo rd || !newPassword || !newPassword2 || '
flash.message ='请输入您当前的密码和新密码'
渲染视图:'changePassword',模型:[用户名:用户名]
返回

SecUser user = SecUser.findByUsername(username)
if(!passwordEncoder.isPasswordValid(user.password,password,null / * salt * /)){
flash.message = 'password password is incorrect'
render view:'changePassword',model:[username:username]
return
}
if(passwordEncoder.isPasswordValid(user.password,newPassword,
flash.message ='请从您当前的密码中选择一个不同的密码'
render view:'changePassword',model:[username:username]
返回
}
if(!newPassword.matches(PASSWORD_REGEX)){
flash.message ='密码不符合最低要求'
渲染视图:'chan gePassword',模型:[用户名:用户名]
返回
}

//如果我们到达这里,成功!
user.password = springSecurityService.encodePassword(newPassword)
user.passwordExpired = false
user.save()

flash.message ='密码更改成功'+ (springSecurityService.loggedIn?'':',你现在可以登录')
redirect uri:'/'
}


Upon creating new users in my system, I am sending them a temporary password via email and setting an property of changePasswordNextLogin=true. When they come to log in for the first time, I would like to intercept the flow upon a successful login, check for this this value, and if it is true, redirect them to a change password action. Once the password change has been completed, ideally I would like to send them to their intended destination.

I have been pouring through the default settings and am not seeing - or more likely not interpreting properly - any way to make that happen. It seems that almost every time that I try to cobble some solution together in Grails, I find that someone has already made a much more elegant approach to do the same thing. Is there any functionality built in that would allow this?

If not, I would really appreciate any suggestions on the best approach to make it so.

解决方案

There is some support for this directly with Spring Security and the grails plugin, but you also have to do some work yourself :)

The domain class that was created when you installed grails-spring-security plugin (and ran the S2Quickstart script) has a property on it named 'passwordExpired'. Set this to true when you create your new user domain instance.

Once that user logs in for the first time, the Spring Security core libs will throw an exception which you can catch in your login controller's authfail closure, re-directing them to the change password form (that you need to supply yourself).

Here's an example from one of my apps, a skeleton version of this closure should already be included in your login controller:

/**
 * Callback after a failed login.
 */
def authfail = {

    def msg = ''

    def username = 
       session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]

    def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]

    if (exception) {
        if (exception instanceof CredentialsExpiredException) {
            msg = g.message(code: "springSecurity.errors.login.passwordExpired")
            if (!springSecurityService.isAjax(request))
                redirect (action:'changePassword') // <-- see below
        }
        // other failure checks omitted
    }

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

/**
 * render the change pwd form
 */
def changePassword = {
    [username: session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY] ?: springSecurityService.authentication.name]
}

From your 'changePasssword' view, submit the form back to another controller closure (I call mine 'updatePassword' that checks whatever constraints you want for passwords and either saves the updated password on the domain object or not..

def updatePassword = {
    String username = session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY] ?: springSecurityService.authentication.name
    if (!username) {
        flash.message = 'Sorry, an error has occurred'
        redirect controller: 'login', action:'auth'
        return
    }
    String password = params.password
    String newPassword = params.password_new
    String newPassword2 = params.password_new_2
    if (!password || !newPassword || !newPassword2 || newPassword != newPassword2) {
        flash.message = 'Please enter your current password and a new password'
        render view: 'changePassword', model: [username: username]
        return
    }
    SecUser user = SecUser.findByUsername(username)
    if (!passwordEncoder.isPasswordValid(user.password, password, null /*salt*/)) {
        flash.message = 'Current password is incorrect'
        render view: 'changePassword', model: [username: username]
        return
    }
    if (passwordEncoder.isPasswordValid(user.password, newPassword, null /*salt*/)) {
        flash.message = 'Please choose a different password from your current one'
        render view: 'changePassword', model: [username: username]
        return
    }
    if (!newPassword.matches(PASSWORD_REGEX)) {
        flash.message = 'Password does not meet minimum requirements'
        render view: 'changePassword', model: [username: username]
        return            
    }

    // success if we reach here!
    user.password = springSecurityService.encodePassword(newPassword)
    user.passwordExpired = false
    user.save() 

    flash.message = 'Password changed successfully' + (springSecurityService.loggedIn ? '' : ', you can now login')
    redirect uri: '/'
}

这篇关于Grails spring-security - 我可以在成功操作之前拦截,以检查所需的密码更改吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆