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,而是转入验证页面(也就是登陆页面)。

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


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值