Как заставить Grails Spring Security Core отображать разные страницы в зависимости от причины отказа в доступе

Я запускаю приложение Grails с помощью подключаемого модуля Spring Security Core.

Когда уже вошедший в систему пользователь пытается получить доступ к странице без доступа к ней, всегда вызывается то же действие, настроенное как 403 в UrlMappings.groovy.

Я изо всех сил пытался заставить мое приложение отображать другую страницу в зависимости от причины отказа в доступе. Например: если требуется IS_AUTHENTICATED_FULLY, я хочу перенаправить пользователя в форму, где он может повторно аутентифицироваться. Если требуется определенная роль, но ее нет, я хочу перенаправить пользователя на страницу, где он может запросить эту роль. И так далее...

Кто-нибудь знает, как это заархивировать?

============================= ОБНОВЛЕНИЕ =================== ===============

Я попытался решить проблему с помощью обратного вызова onAuthorizationEven‌​t, описанного в документы. К сожалению, параметр события всегда идентичен независимо от правила, которое его активировало.

По крайней мере, у меня есть доступ к URI, которому было отказано в доступе оттуда. Есть ли способ получить правила безопасности из URI, чтобы я мог сравнить с текущими ролями и статусом пользователя и выяснить, чего не хватает? Это, возможно, решило бы проблему.


person ylima    schedule 15.08.2016    source источник


Ответы (2)


После долгих исследований в Интернете, наконец, удалось реализовать некоторые идеи из ответа @yariash и этот пост:

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
    }
}
person ylima    schedule 16.08.2016
comment
Спасибо. Только что сделал ;) - person ylima; 18.08.2016

Может быть, создать контроллер и доступ к исключению там?

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


class ErrorController {  
   def error403() {
       def message = request.exception?.cause?.message; //access message and render right view
   }
}
person Krzysztof Atłasik    schedule 16.08.2016
comment
Эй, спасибо за быстрый ответ. Пробовал. К сожалению, исключение всегда равно нулю. У меня есть доступ к AccessDeniedException, выброшенному Spring Security в обратном вызове onAuthorizationEvent. Но это идентично независимо от правила, которое его вызвало. Я отредактирую свой вопрос с более подробной информацией. - person ylima; 16.08.2016