Spring security AccessDeniedHandler 不被调用

1.问题

在使用Spring Security时,在applicationContext-security.xml中配置了accecc-denied-handler:

	<!-- 自动配置模式,拦截所有请求,有ROLE_USER才可以通过  -->
	<http auto-config="true" >
	
		<intercept-url pattern="/login**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
		<intercept-url pattern="/js/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
		
		<intercept-url pattern="/**" access="ROLE_USER" /> 

		<form-login login-page="/login.html" authentication-failure-url ="/loginfailure" default-target-url="/loginsuccess"/>
		<logout invalidate-session="true" logout-success-url="/login.html" logout-url="/j_spring_security_logout"/>


		<span style="background-color: rgb(255, 0, 0);"><strong><access-denied-handler ref="myAuthenticationFailureHandler"/></strong></span>

	</http>
	
    <beans:bean id="myAuthenticationFailureHandler" class="com.lenovo.MyAuthenticationFailureHandler" />

而且AccessDecisionManager模块明明抛出了AccessDeniedException:

public class MyAccessDecisionManager implements AccessDecisionManager {

    /**
     * @author ligh4 2015年3月31日下午5:28:21
     */
    @Override
    public void decide(Authentication arg0, Object arg1, Collection<ConfigAttribute> arg2)
            throws AccessDeniedException {

        if (arg2 == null) {
            return;
        }
        LogHelper.debug(this, arg1.toString()); //object is a URL.
        Iterator<ConfigAttribute> ite = arg2.iterator();
        while (ite.hasNext()) {
            ConfigAttribute ca = ite.next();
            String needRole = ((SecurityConfig) ca).getAttribute();
            for (GrantedAuthority ga : arg0.getAuthorities()) {
                if (needRole.equals(ga.getAuthority())) { //ga is user's role.
                    return;
                }
            }
        }
<strong><span style="color:#FF0000;">        LogHelper.warn(this, "No right of url:" + arg1.toString());
        throw new AccessDeniedException("no right");</span></strong>

    }

    /**
     * @author ligh4 2015年3月31日下午5:28:21
     */
    @Override
    public boolean supports(ConfigAttribute arg0) {
        // TODO Auto-generated method stub
        return true;
    }

    /**
     * @author ligh4 2015年3月31日下午5:28:21
     */
    @Override
    public boolean supports(Class<?> arg0) {
        // TODO Auto-generated method stub
        return true;
    }

}
但是MyAuthenticationFailureHandler中却无法捕获该异常:

/**
 * 类 MyAuthenticationFailureHandler 的实现描述:TODO 类实现描述
 * 
 * @author ligh4 2015年3月31日下午4:04:40
 */
public class MyAuthenticationFailureHandler implements AccessDeniedHandler {

    /**
     * @author ligh4 2015年3月31日下午4:15:59
     */
    @Override
    public void handle(HttpServletRequest arg0, HttpServletResponse arg1, AccessDeniedException arg2)
            throws IOException, ServletException {

        LogHelper.debug(this, "handler AccessDeniedException...");

        HttpServletRequest httpRequest = arg0;
        // is ajax request?
        if ("XMLHttpRequest".equals(httpRequest.getHeader("X-Requested-With"))) {
            String msg = "{\"success\" : false, \"message\" : \"authentication-failure\"}";

            arg1.setContentType("json");
            OutputStream outputStream = arg1.getOutputStream();
            outputStream.write(msg.getBytes());
            outputStream.flush();
        }
    }

}

2.原因

参考:http://stackoverflow.com/questions/7013197/spring-3-security-accessdeniedhandler-is-not-being-invoked

原文内容如下:

AccessDeniedHandler is invoked when user is logged in and there is no permissions to resource (source here). If you want to handle request for login page when user is not logged in, just configure in security-context:

<http ... entry-point-ref="customAuthenticationEntryPoint">

And define customAuthenticationEntryPoint:

<beans:bean id="customAuthenticationEntryPoint" class="pl.wsiadamy.webapp.controller.util.CustomAuthenticationEntryPoint">
</beans:bean>

TIP, don‘t try to fight with ExceptionTranslationFilter.I have tried to override org.springframework.security.web.access.ExceptionTranslationFilter, without effects:

<beans:bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
  <beans:property name="authenticationEntryPoint"  ref="customAuthenticationEntryPoint"/>
  <beans:property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</beans:bean>
<beans:bean id="accessDeniedHandler"
 class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
  <beans:property name="errorPage" value="/accessDenied.htm"/>
</beans:bean>

The ref="customAuthenticationEntryPoint" just didn‘t invoked.


就是说只有确实的访问失败才会进入AccessDeniedHandler,如果是未登陆或者会话超时等,不会触发AccessDeniedHandler,而是会直接跳转到登陆页面。具体可参见ExceptionTranslationFilter中的处理:

doFilter函数:

<span style="color:#000000;">public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
            chain.doFilter(request, response);

            logger.debug("Chain processed normally");
        }
        catch (IOException ex) {
            throw ex;
        }
        catch (Exception ex) {
            // Try to extract a SpringSecurityException from the stacktrace
            Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
            RuntimeException ase = (AuthenticationException)
                    throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);

            if (ase == null) {
                ase = (AccessDeniedException)throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
            }

            if (ase != null) {
                <strong><span style="color:#FF0000;">handleSpringSecurityException(request, response, chain, ase);</span></strong>
            } else {
                // Rethrow ServletExceptions and RuntimeExceptions as-is
                if (ex instanceof ServletException) {
                    throw (ServletException) ex;
                }
                else if (ex instanceof RuntimeException) {
                    throw (RuntimeException) ex;
                }

                // Wrap other Exceptions. This shouldn't actually happen
                // as we've already covered all the possibilities for doFilter
                throw new RuntimeException(ex);
            }
        }
    }</span>

handleSpringSecurityException函数:

<span style="color:#000000;">private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            RuntimeException exception) throws IOException, ServletException {
        if (exception instanceof AuthenticationException) {
            logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);

            sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
        }
        else if (exception instanceof AccessDeniedException) {
            <strong><span style="color:#FF0000;">if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication()))</span></strong> {
                logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point",
                            exception);

                <strong><span style="color:#FF0000;">sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException(
                        "Full authentication is required to access this resource"));</span></strong>
            }
            else {
                logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception);

                accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
            }
        }
    }</span>

根本不会调用accessDeniedHandler,而是转入验证页面(也就是登陆页面)。

所以使用时还是要注意区分登陆失败和没有权限访问的情况。


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。