Spring Security使用方法级安全性忽略访问被拒绝处理程序

时间:2014-01-16 20:12:13

标签: spring-mvc spring-security

我正在使用Spring 3.2.4,并且在使用基于Annotation的方法级安全性时无法让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/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>

我的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;
}

我需要做些什么特别的事情来调用我的访问被拒绝处理程序吗?

4 个答案:

答案 0 :(得分:19)

经过几个小时的搜索和追踪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来处理异常。< / p>

答案 1 :(得分:5)

我遇到了同样的问题。在我的情况下,已经有一个@ControllerAdvise,它应该处理异常 - 所以我直接添加了AccessDeniedException:

@Component
@ControllerAdvice
public class ControllerBase {

...

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

答案 2 :(得分:2)

使用JavaConfig扩展Erics答案,使SimpleMappingExceptionResolver忽略AccessDeniedException,以便它可以作为响应抛出,并且不会被SimpleMappingExceptionResolver吞噬。

@Configuration
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {

  @Bean
  public SimpleMappingExceptionResolver exceptionResolver() {
    SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
    exceptionResolver.setExcludedExceptions(AccessDeniedException.class);
    return exceptionResolver;
  }

}

答案 3 :(得分:0)

添加到上述Jessi答案(https://stackoverflow.com/a/25948861/13215486)上。请注意,如果您要区分“拒绝访问”和“禁止访问”之间的区别,则需要做更多的工作。

@Component
@ControllerAdvice
public class ControllerBase {

...

  @ExceptionHandler(value = AccessDeniedException.class)
    public ModelAndView accessDenied(HttpServletRequest request) {
        ModelAndView mav = new ModelAndView("redirect:login.html");
        mav.setStatus(request.getRemoteUser() != null ? HttpStatus.FORBIDDEN : HttpStatus.UNAUTHORIZED);
        return mav;
    }
}