3

在我的 grails 应用程序中,我通过编写自定义身份验证成功处理程序(在 resources.groovy 中)自定义了授权后工作流程,如下所示。

authenticationSuccessHandler (MyAuthSuccessHandler) {
    def conf = SpringSecurityUtils.securityConfig
    requestCache = ref('requestCache')
    defaultTargetUrl = conf.successHandler.defaultTargetUrl
    alwaysUseDefaultTargetUrl = conf.successHandler.alwaysUseDefault
    targetUrlParameter = conf.successHandler.targetUrlParameter
    useReferer = conf.successHandler.useReferer
    redirectStrategy = ref('redirectStrategy')
    superAdminUrl = "/admin/processSuperAdminLogin"
    adminUrl = "/admin/processAdminLogin"
    userUrl = "/admin/processUserLogin"
}

正如您可以从上面闭包的最后三行中看到的那样,根据授予登录用户的角色,我将她重定向到AdminController中的单独操作,其中创建自定义 UserSessionBean 并将其存储在会话中。

它适用于我的应用程序中的常规登录案例,如下所示:

  1. 用户通过http://localhost:8080/my-app/OR进入应用程序http://localhost:8080/my-app/login/auth
  2. 她输入她的有效登录 ID 和密码并继续。
  3. 应用程序在内部访问 MyAuthSuccessHandler,考虑到授予此用户的角色,该处理程序重定向到 AdminController。
  4. 创建 UserSessionBean 并将其存储在会话中
  5. 用户被带到应用主页

我还MyUserDetailsService通过扩展编写了一个自定义,GormUserDetailsService在上述流程中可以正确访问该自定义。

问题场景:
考虑用户直接访问应用程序内的受保护资源(在这种情况下,控制器通过@Secured注释保护)。

  1. 用户点击http://localhost:8080/my-app/inbox/index
  2. 应用程序将她重定向到http://localhost:8080/my-app/login/auth
  3. 用户输入她的有效登录 ID 和密码
  4. 用户被带到http://localhost:8080/my-app/inbox/index

在这个MyAuthSuccessHandler过程中完全跳过了,因此我的UserSessionBean没有被创建,导致在访问UserSessionBean的地方进一步使用时出错。

问题:

  1. 在问题场景中,应用程序是否跳过了,MyAuthSuccessHandler因为有一个目标 URL 可以在登录时重定向到?
  2. MyAuthSuccessHandler即使存在目标 URL,我们是否可以强制该过程始终通过?
  3. 如果对 2 的回答是否定的,那么关于如何以及在何处仍然可以创建UserSessionBean是否有替代方法?
4

2 回答 2

4

您可以实现自定义的 eventListener 来处理登录后过程,而不会破坏原始用户请求的 url。

在 config.groovy 中,插入一个配置项:

grails.plugins.springsecurity.useSecurityEventListener = true

在你的 resources.groovy 中,添加一个像这样的 bean:

import com.yourapp.auth.LoginEventListener
beans = {
    loginEventListener(LoginEventListener)
}

并在 src/groovy 中创建一个 eventListener,如下所示:

package com.yourapp.auth
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent
import org.springframework.web.context.request.RequestContextHolder as RCH

class LoginEventListener implements
    ApplicationListener<InteractiveAuthenticationSuccessEvent> {    

    //deal with successful login    
    void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
        User.withTransaction { 
            def user = User.findByUsername(event.authentication.principal.username)
                    def adminRole = Role.findByAuthority('ROLE_ADMIN')
                    def userRole = Role.findByAuthority('ROLE_USER')
                    def session = RCH.currentRequestAttributes().session      //get httpSession
                    session.user = user
                    if(user.authorities.contains(adminRole)){
                        processAdminLogin()
                    }
                    else if(user.authorities.contains(userRole)){
                        processUserLogin()
                    }
        }
    }

    private void processAdminLogin(){    //move admin/processAdminLogin here
          .....
    }

    private void processUserLogin(){    //move admin/processUserLogin here
          .....
    }
}

完毕。

于 2012-12-17T05:44:34.390 回答
3

1) 是的,因为它是“按需”登录。

2)是的,您可以将其设置为始终使用默认值。Spring 安全插件有一个设置“ successHandler.alwaysUseDefault ”,将其更改为 true,默认为 false。

此外,如果您需要更多详细信息,请查看 spring文档以查找设置默认登录后目标部分。

3) 如果您仍想创建用户会话 bean,然后重定向到原始 URL,您有两个选项在较早的过滤器中创建 bean 或通过自定义UserDetailsS​​ervice公开所需的数据。我个人会走自定义详细信息服务的路线。

于 2012-12-07T06:25:55.217 回答