Grails Spring安全性:使用目标URL登录会跳过认证后工作流
在我的grails应用程序中,我通过编写自定义的身份验证成功处理程序(在resources.groovy中)定制了授权后工作流,如下所示Grails Spring安全性:使用目标URL登录会跳过认证后工作流,grails,spring-security,Grails,Spring Security,在我的grails应用程序中,我通过编写自定义的身份验证成功处理程序(在resources.groovy中)定制了授权后工作流,如下所示 authenticationSuccessHandler (MyAuthSuccessHandler) { def conf = SpringSecurityUtils.securityConfig requestCache = ref('requestCache') defaultTargetUrl = conf.successHan
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
它适用于常规登录情况,在我的应用程序中如下所示:
http://localhost:8080/my-app/
或http://localhost:8080/my-app/login/auth
GormUserDetailsService
编写了一个自定义MyUserDetailsService
问题场景:考虑一个用户直接访问受保护资源(在这种情况下,控制器在应用程序中以<代码> @安全< /代码>注释)进行保护。
http://localhost:8080/my-应用程序/收件箱/索引
http://localhost:8080/my-app/login/auth
http://localhost:8080/my-应用程序/收件箱/索引
MyAuthSuccessHandler
被完全跳过,因此我的UserSessionBean不会被创建,从而导致在访问UserSessionBean的地方进一步使用时出错
问题:
MyAuthSuccessHandler
,因为它有一个登录时要重定向到的目标URLMyAuthSuccessHandler
3) 如果您仍然希望创建用户会话bean,然后重定向到原始URL,那么您有两个选项:在前面的过滤器中创建bean,或者通过自定义的方法公开所需的数据。就我个人而言,我会选择自定义详细信息服务。您可以实现自定义的eventListener来处理登录后的过程,而不会中断原始用户请求的url 在config.groovy中,插入一个配置项:
grails.plugins.springsecurity.useSecurityEventListener = true
在resources.groovy中,添加如下bean:
import com.yourapp.auth.LoginEventListener
beans = {
loginEventListener(LoginEventListener)
}
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
.....
}
}
并在src/groovy中创建一个eventListener,如下所示:
import com.yourapp.auth.LoginEventListener
beans = {
loginEventListener(LoginEventListener)
}
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
.....
}
}
package com.yourapp.auth
导入org.springframework.context.ApplicationListener;
导入org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent
将org.springframework.web.context.request.RequestContextHolder作为RCH导入
类LoginEventListener实现
应用程序侦听器{
//处理成功登录
Application event上的无效(InteractiveAuthenticationSuccessEvent事件){
User.withTransaction{
def user=user.findByUsername(event.authentication.principal.username)
def adminRole=Role.findByAuthority('Role\u ADMIN')
def userRole=Role.findByAuthority('Role\u USER')
def session=RCH.currentRequestAttributes().session//get-httpSession
session.user=用户
if(user.authorities.contains(adminRole)){
processAdminLogin()
}
else if(user.authorities.contains(userRole)){
processUserLogin()
}
}
}
私有void processAdminLogin(){//move admin/processAdminLogin此处
.....
}
私有void processUserLogin(){//move admin/processUserLogin此处
.....
}
}
完成。是
conf.successHandler.AlwaysSuseDefault
设置为true
?我没有更改它。它处于默认状态。感谢您的快速回答。我不知道alwaysUseDefault配置值,现在已经相应地设置了它。虽然现在它将始终重定向到默认URL(在本例中为应用程序主页)。在alwaysUseDefault设置为true的情况下,是否有办法将用户重定向到请求的URL?我编辑了答案并为选项3添加了答案,这可能更符合您的要求。@JeffBeck在我的场景中,自定义UserDetailsService也以与SuccessHandler相同的方式被绕过。这似乎是一个更好的选项,除了在这个类中有没有访问HttpSession的方法?如前所述,我必须在其中存储一个UserSessionBeanabove@SagarV当然,您可以像def session=RequestContextHolder.currentRequestAttributes().session
那样访问它。我更新了我的示例以显示该情况。