如何使 Grails 的 Spring 安全核心根据访问被拒绝的原因呈现不同的页面

How to make Grails's Spring Security Core render a different page depending on the reason why access was denied

我是 运行 使用 Spring 安全核心插件的 Grails 应用程序。

当一个已经登录的用户尝试访问一个页面而不访问它时,总是调用在 UrlMappings.groovy 中配置为 403 的相同操作。

我一直在努力让我的应用程序根据拒绝访问的原因呈现不同的页面。例如:如果需要 IS_AUTHENTICATED_FULLY 我想将用户重定向到一个他可以重新验证的表单。如果需要特定角色但不存在,我想将用户重定向到他可以请求此角色的页面。等等...

有人知道如何存档吗?

==============================更新============== ====================

我试图通过 docs 中描述的 onAuthorizationEven‌​t 回调解决问题。不幸的是,无论触发它的规则如何,事件参数始终相同。

至少我可以访问被拒绝访问的 URI。有没有办法从 URI 获取安全规则,以便我可以与当前用户的角色和状态进行比较并找出缺少的内容?那也许可以解决问题。

也许在那里创建控制器和访问异常?

static mappings = {
    //....
   "403"(controller:'error', action:'error403')
   //......
}


class ErrorController {  
   def error403() {
       def message = request.exception?.cause?.message; //access message and render right view
   }
}

通过互联网进行了长时间的研究后,终于根据@yariash 的回复和 this post:

中的一些想法使其工作
import org.springframework.context.ApplicationListener;
import org.springframework.security.access.event.AuthorizationFailureEvent
import org.springframework.security.authentication.RememberMeAuthenticationToken;

class AuthorizationFailureEventListener
        implements ApplicationListener<AuthorizationFailureEvent> {

    @Override
    public void onApplicationEvent(AuthorizationFailureEvent e) {
        def attributes = e.getConfigAttributes()

        if(!attributes) {
            return
        }

        def authentication = e.getAuthentication()
        def requiredAuthorities = attributes?.collect { it.getAttribute() }
        def userAuthorities = authentication.getAuthorities().collect { it.getAuthority() }
        def missingAuthorities = requiredAuthorities - userAuthorities

        if(requiredAuthorities.contains('IS_AUTHENTICATED_FULLY') &&
                !(authentication instanceof RememberMeAuthenticationToken)) {
            requiredAuthorities.remove('IS_AUTHENTICATED_FULLY')
        }

        e.getSource().getRequest().setAttribute("MISSING_AUTHORITIES", missingAuthorities);
    }

}

然后将此侦听器作为 bean 包括在内:

beans = {
    //...
    authorizationFailureEventListener(AuthorizationFailureEventListener) { bean ->
        bean.autowire = "byName"
    }
    //...
}

终于在我的错误控制器中:

static mappings = {
    //....
    "403"(controller:'error', action:'error403')
    //......
}

class ErrorController {  
    def error403() {
        def missingAuthorities = request.getAttribute("MISSING_AUTHORITIES")
        // Render the right view based on the missing authorities
    }
}