`
Dead_knight
  • 浏览: 1193939 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
博客专栏
752c8642-b795-3fe6-946e-a4e845bffdec
Spring Securi...
浏览量:238264
33caa84e-18a6-3036-a82b-6e2106a4de63
clojure专题
浏览量:48081
E17ca077-44df-3816-a3fe-471c43f6e1e5
WebLogic11g
浏览量:235930
社区版块
存档分类
最新评论

Spring Security3源码分析-ExceptionTranslationFilter分析

阅读更多
ExceptionTranslationFilter过滤器对应的类路径为
org.springframework.security.web.access.ExceptionTranslationFilter
从类名就看出这个过滤器用于异常翻译的。但是从这个过滤器在filterchain中的位置来看,它仅仅处于倒数第三的位置(这个filter后面分为是FilterSecurityInterceptor、SwitchUserFilter),所以ExceptionTranslationFilter只能捕获到后面两个过滤器所抛出的异常。
这里需要强调一下,spring security中的异常类基本上都继承RuntimeException。

接着看ExceptionTranslationFilter执行过程
    //doFilter拦截到请求时,不做处理。仅仅处理后面filter所抛出的异常
    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);
        }
        catch (IOException ex) {
            throw ex;
        }
        catch (Exception ex) {
            //这里主要是从异常堆栈中提取SpringSecurityException
            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) {
                handleException(request, response, chain, ase);
            } else {
                //没有安全异常,继续抛出
                // Rethrow ServletExceptions and RuntimeExceptions as-is
                if (ex instanceof ServletException) {
                    throw (ServletException) ex;
                }
                else if (ex instanceof RuntimeException) {
                    throw (RuntimeException) ex;
                }
                throw new RuntimeException(ex);
            }
        }
    }
    //处理安全异常
    private void handleException(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            RuntimeException exception) throws IOException, ServletException {
        //如果是认证异常,由sendStartAuthentication处理
        if (exception instanceof AuthenticationException) {
            sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
        }
        //如果是访问拒绝异常,由访问拒绝处理类的handle处理
        else if (exception instanceof AccessDeniedException) {
            if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
                sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException(
                        "Full authentication is required to access this resource"));
            }
            else {
                accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
            }
        }
    }

先分析如何处理认证异常
    //处理认证异常
    protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            AuthenticationException reason) throws ServletException, IOException {
        // SEC-112: Clear the SecurityContextHolder's Authentication, as the
        // existing Authentication is no longer considered valid
        //首先把SecurityContext中的认证实体置空
        SecurityContextHolder.getContext().setAuthentication(null);
        //通过cache保存当前的请求信息(分析RequestCacheAwareFilter时再深入)
        requestCache.saveRequest(request, response);
        logger.debug("Calling Authentication entry point.");
        //由认证入口点开始处理
        authenticationEntryPoint.commence(request, response, reason);
    }

这里补充一下
authenticationEntryPoint是由配置http标签时,通过什么认证入口来决定注入相应的入口点bean的。请看下面的对应关系列表
form-login认证:LoginUrlAuthenticationEntryPoint
http-basic认证:BasicAuthenticationEntryPoint
openid-login认证:LoginUrlAuthenticationEntryPoint
x509认证:Http403ForbiddenEntryPoint


就不一一分析每个EntryPoint了,着重看一下LoginUrlAuthenticationEntryPoint
    //主要目的是完成跳转任务
     //创建该bean时,只注入了loginFormUrl属性,其他类变量均为默认值
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String redirectUrl = null;
        //默认为false
        if (useForward) {
            if (forceHttps && "http".equals(request.getScheme())) {
                redirectUrl = buildHttpsRedirectUrlForRequest(httpRequest);
            }

            if (redirectUrl == null) {
                String loginForm = determineUrlToUseForThisRequest(httpRequest, httpResponse, authException);
                RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginForm);
                dispatcher.forward(request, response);
                return;
            }
        } else {
            //返回的url为loginFormUrl配置的值,如果未配置,跳转到默认登录页面/spring_security_login
            redirectUrl = buildRedirectUrlToLoginPage(httpRequest, httpResponse, authException);

        }
        redirectStrategy.sendRedirect(httpRequest, httpResponse, redirectUrl);
    }


接着分析访问拒绝类异常的处理过程,看AccessDeniedHandlerImpl的handle方法
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
            throws IOException, ServletException {
        if (!response.isCommitted()) {
            //如果配置了access-denied-page属性,跳转到指定的url
            if (errorPage != null) {
                // Put exception into request scope (perhaps of use to a view)
                request.setAttribute(SPRING_SECURITY_ACCESS_DENIED_EXCEPTION_KEY, accessDeniedException);

                // Set the 403 status code.
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);

                // forward to error page.
                RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
                dispatcher.forward(request, response);
            //如果没有配置,则直接响应403禁止访问的错误信息到浏览器端
            } else {
                response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
            }
        }
    }


通过以上分析,可以大体上认识到ExceptionTranslationFilter主要拦截两类安全异常:认证异常、访问拒绝异常。而且仅仅是捕获FilterSecurityInterceptor、SwitchUserFilter以及自定义拦截器的异常。所以在自定义拦截器时,需要注意在链中的顺序。

在上面分析过程中,有requestCache.saveRequest(request, response);的语句,具体requestCache的用途下篇分析。
分享到:
评论

相关推荐

    Spring Security-3.0.1中文官方文档(翻译版)

    Spring Security-3.0.1 中文官方文档(翻译版) 这次发布的Spring Security-3.0.1 是一个bug fix 版,主要是对3.0 中存在的一些问题进 行修 正。文档中没有添加新功能的介绍,但是将之前拼写错误的一些类名进行...

    Spring Security 中文教程.pdf

    1.1. Spring Security是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. ...

    spring security 参考手册中文版

    3. Spring Security 4.2的新特性 27 3.1 Web改进 27 3.2配置改进 28 3.3杂项 28 4.样品和指南(从这里开始) 28 5. Java配置 29 5.1 Hello Web安全Java配置 29 5.1.1 AbstractSecurityWebApplicationInitializer 31 ...

    SpringSecurity 3.0.1.RELEASE.CHM

    1.1. Spring Security是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. Config -...

    acegi认证,授权

    /**=httpSessionContextIntegrationFilter,basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor class="org.acegisecurity.context....

Global site tag (gtag.js) - Google Analytics