Spring Безопасность, игнорирующая обработчик отказа от доступа с защитой уровня метода
Я использую Spring 3.2.4 и не могу получить Spring Security для перенаправления на мой access-denied-handler
при использовании безопасности уровня на основе аннотаций. Я нашел несколько разных сообщений об этом, но на сегодняшний день не найдено никаких решений, которые я нашел.
Мой файл security.xml:
<!-- need this here to be able to secure methods in components other than controllers (as scanned in applicationContext.xml) -->
<global-method-security secured-annotations="enabled" pre-post-annotations="enabled" jsr250-annotations="enabled" ></global-method-security>
<!-- Annotation/JavaConfig examples http://stackoverflow.com/info/7361513/spring-security-login-page -->
<http use-expressions="true" entry-point-ref="authenticationEntryPoint">
<access-denied-handler ref="accessDeniedHandler"/>
<intercept-url pattern="/secure/login" access="permitAll" />
<intercept-url pattern="/secure/logout" access="permitAll" />
<intercept-url pattern="/secure/denied" access="permitAll" />
<session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.jsp?authFailed=true">
<concurrency-control max-sessions="10" error-if-maximum-exceeded="true" expired-url="/login.html" session-registry-alias="sessionRegistry"/>
</session-management>
<intercept-url pattern="/**" access="isAuthenticated()" />
<form-login default-target-url="/" authentication-failure-url="/secure/denied" />
<logout logout-url="/secure/logout" logout-success-url="/" />
<expression-handler ref="defaultWebSecurityExpressionHandler" />
</http>
<beans:bean id="authenticationEntryPoint" class="com.ia.security.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg name="loginFormUrl" value="/secure/login"/>
</beans:bean>
<beans:bean id="accessDeniedHandler" class="com.ia.security.AccessDeniedHandlerImpl">
<beans:property name="errorPage" value="/secure/denied"/>
</beans:bean>
My AccessDeniedHandlerImpl.java:
public class AccessDeniedHandlerImpl extends org.springframework.security.web.access.AccessDeniedHandlerImpl {
// SLF4J logger
private static final Logger logger = LoggerFactory.getLogger(AccessDeniedHandlerImpl.class);
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
logger.log("AccessDeniedException triggered!");
super.handle(request, response, accessDeniedException);
}
}
Мой аннотированный метод:
@PreAuthorize("hasAuthority('ROLE_ZZZZ')")
public ModelAndView getUserInfo( @PathVariable long userId ){
ModelAndView mv = new ModelAndView();
User u = userService.findUser( userId );
mv.addObject("user", u);
return mv;
}
Есть ли что-то особенное, что мне нужно сделать так, чтобы вызывается мой обработчик отказа от доступа?
Ответы
Ответ 1
После нескольких часов поиска и отслеживания кода Spring я, наконец, обнаружил, что происходит. Я перечисляю это здесь, если это ценно для кого-то другого.
access-denied-handler
используется ExceptionTranslationFilter
в случае AccessDeniedException
. Однако org.springframework.web.servlet.DispatcherServlet
сначала попытался обработать исключение. В частности, у меня был org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
, определенный с помощью defaultErrorView
. Следовательно, SimpleMappingExceptionResolver
потребляло исключение, перенаправляя соответствующее представление, и, следовательно, не было никаких исключений, чтобы пузыриться до ExceptionTranslationFilter
.
Исправление было довольно простым. Настройте SimpleMappingExceptionResolver
, чтобы игнорировать все AccessDeniedException
.
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="uncaughtException" />
<property name="excludedExceptions" value="org.springframework.security.access.AccessDeniedException" />
<property name="exceptionMappings">
<props>
<prop key=".DataAccessException">dataAccessFailure</prop>
<prop key=".NoSuchRequestHandlingMethodException">resourceNotFound</prop>
<prop key=".TypeMismatchException">resourceNotFound</prop>
<prop key=".MissingServletRequestParameterException">resourceNotFound</prop>
</props>
</property>
</bean>
Теперь, когда генерируется AccessDeniedException
, распознаватель игнорирует его и позволяет ему сбрасывать стек в ExceptionTranslationFilter
, который затем вызывает access-denied-handler
для обработки исключения.
Ответ 2
Я столкнулся с той же проблемой. В моем случае уже было определено @ControllerAdvise, которое должно обрабатывать исключения - поэтому я добавил AccessDeniedException напрямую:
@Component
@ControllerAdvice
public class ControllerBase {
...
@ExceptionHandler(value = AccessDeniedException.class)
public ModelAndView accessDenied() {
return new ModelAndView("redirect:login.html");
}
}
Удачи вам!
Ответ 3
Расширение ответа Erics с помощью JavaConfig для SimpleMappingExceptionResolver
, чтобы игнорировать AccessDeniedException
, чтобы его можно было отбросить как ответ и не усвоить SimpleMappingExceptionResolver.
@Configuration
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
@Bean
public SimpleMappingExceptionResolver exceptionResolver() {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
exceptionResolver.setExcludedExceptions(AccessDeniedException.class);
return exceptionResolver;
}
}