Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security ignoring access-denied-handler with Method Level Security

I'm using Spring 3.2.4 and am unable to get Spring Security to redirect to my access-denied-handler when using Annotation based method level security. I have found several different posts about this, but to date, there does not seem to be any solutions that I have found.

My security.xml file:

<!-- 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/questions/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);

    }
}

My Annotated Method:

@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;
}

Is there anything special I need to do such that my access-denied-handler is called?

like image 896
Eric B. Avatar asked Jan 16 '14 20:01

Eric B.


2 Answers

After several hours of searching around and tracing Spring code, I finally discovered what was happening. I am listing this here in case it is of value to someone else.

The access-denied-handler is used by the ExceptionTranslationFilter in case of an AccessDeniedException. However, the org.springframework.web.servlet.DispatcherServlet was first trying the handle the exception. Specifically, I had a org.springframework.web.servlet.handler.SimpleMappingExceptionResolver defined with a defaultErrorView. Consequently, the SimpleMappingExceptionResolver was consuming the exception by redirecting to an appropriate view, and consequently, there was no exception left to bubble up to the ExceptionTranslationFilter.

The fix was rather simple. Configure the SimpleMappingExceptionResolver to ignore all 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>

Now, whenever an AccessDeniedException is thrown, the resolver ignores it and allows it to bubble up the stack to the ExceptionTranslationFilter which then calls upon the access-denied-handler to handle the exception.

like image 57
Eric B. Avatar answered Sep 19 '22 12:09

Eric B.


I run into the same issue. In my case there was already a @ControllerAdvise definied which should handle exceptions - so I added the AccessDeniedException directly:

@Component
@ControllerAdvice
public class ControllerBase {

...

  @ExceptionHandler(value = AccessDeniedException.class)
    public ModelAndView accessDenied() {
        return new ModelAndView("redirect:login.html");
    }
}

Good luck with it!

like image 42
Jessi Avatar answered Sep 20 '22 12:09

Jessi