`
kobe学java
  • 浏览: 249953 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

spring security 源码分析: 过滤器

 
阅读更多

首先 请求进入 FilterChainProxy 这个类

 

   FilterChainProxy.java

Java代码   收藏代码
  1. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  
  2.            throws IOException, ServletException {  
  3.   
  4.        FilterInvocation fi = new FilterInvocation(request, response, chain);  
  5.        List<Filter> filters = getFilters(fi.getRequestUrl());  
  6.   
  7.        if (filters == null || filters.size() == 0) {  
  8.            if (logger.isDebugEnabled()) {  
  9.                logger.debug(fi.getRequestUrl() +  
  10.                        filters == null ? " has no matching filters" : " has an empty filter list");  
  11.            }  
  12.   
  13.            chain.doFilter(request, response);  
  14.   
  15.            return;  
  16.        }  
  17.   
  18.        VirtualFilterChain virtualFilterChain = new VirtualFilterChain(fi, filters);  
  19.        virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());  
  20.    }  
  21. ublic List<Filter> getFilters(String url)  {  
  22.        if (stripQueryStringFromUrls) {  
  23.            // String query string - see SEC-953  
  24.            int firstQuestionMarkIndex = url.indexOf("?");  
  25.   
  26.            if (firstQuestionMarkIndex != -1) {  
  27.                url = url.substring(0, firstQuestionMarkIndex);  
  28.            }  
  29.        }  
  30.   
  31.        for (Map.Entry<Object, List<Filter>> entry : filterChainMap.entrySet()) {  
  32.            Object path = entry.getKey();  
  33.   
  34.            if (matcher.requiresLowerCaseUrl()) {  
  35.                url = url.toLowerCase();  
  36.   
  37.                if (logger.isDebugEnabled()) {  
  38.                    logger.debug("Converted URL to lowercase, from: '" + url + "'; to: '" + url + "'");  
  39.                }  
  40.            }  
  41.   
  42.            boolean matched = matcher.pathMatchesUrl(path, url);  
  43.   
  44.            if (logger.isDebugEnabled()) {  
  45.                logger.debug("Candidate is: '" + url + "'; pattern is " + path + "; matched=" + matched);  
  46.            }  
  47.   
  48.            if (matched) {  
  49.                return entry.getValue();  
  50.            }  
  51.        }  
  52.   
  53.        return null;  
  54.    }  

 

   可以看出, FilterInvocation 是见 request 和 response ,chain 只是进行了封装, 然后根据 url 来判断这个请求是否需要进行拦截, 这里 getFilter() 方法是查询的 intercepter-url 中配置的 内容。(这里具体的内容在下面)

   接下来就是执行所有的List<Filter> 。执行完所有的List<Filter>之后会继续执行容器的filterChain

   VirtualFilterChain.java 这是 FilterChainProxy 的内部类

Java代码   收藏代码
  1. public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {  
  2.           <span style="color: #ff0000;">  if (currentPosition == additionalFilters.size()) {  
  3.                </span> if (logger.isDebugEnabled()) {  
  4.                     logger.debug(fi.getRequestUrl()  
  5.                         + " reached end of additional filter chain; proceeding with original chain");  
  6.                 }  
  7. <span style="color: #ff0000;">  
  8.                 fi.getChain().doFilter(request, response);</span>  
  9.             } else {  
  10.                 currentPosition++;  
  11.   
  12.                 Filter nextFilter = additionalFilters.get(currentPosition - 1);  
  13.   
  14.                 if (logger.isDebugEnabled()) {  
  15.                     logger.debug(fi.getRequestUrl() + " at position " + currentPosition + " of "  
  16.                         + additionalFilters.size() + " in additional filter chain; firing Filter: '"  
  17.                         + nextFilter + "'");  
  18.                 }  
  19.   
  20.                nextFilter.doFilter(request, response, this);  
  21.             }  
  22.         }  
 

   下面先按顺序分析各Filter的作用 (security默认添加的filterChain,共11个 还有大概4,5个没有涉及到,以后涉及到再进行添加)

      1.org.springframework.security.web.context.SecurityContextPersistenceFilter

          (2.0中是这个HttpSessionContextIntegrationFilter

        从这个类所在的包路径  context,大致知道这个类 只处理 上下文 

Java代码   收藏代码
  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)            
  2.             throws IOException, ServletException {  
  3.        HttpServletRequest request = (HttpServletRequest) req;  
  4.        HttpServletResponse response = (HttpServletResponse) res;  
  5.   
  6.        if (request.getAttribute(FILTER_APPLIED) != null) {  
  7.            // ensure that filter is only applied once per request  
  8.            chain.doFilter(request, response);  
  9.            return;  
  10.        }  
  11.   
  12.        final boolean debug = logger.isDebugEnabled();  
  13.   
  14.        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);  
  15.   
  16.        if (forceEagerSessionCreation) {  
  17.            HttpSession session = request.getSession();  
  18.   
  19.            if (debug && session.isNew()) {  
  20.                logger.debug("Eagerly created session: " + session.getId());  
  21.            }  
  22.        }  
  23.   
  24.        HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);  
  25.        SecurityContext contextBeforeChainExecution = repo.loadContext(holder);  
  26.   
  27.        try {  
  28.            SecurityContextHolder.setContext(contextBeforeChainExecution);  
  29.   
  30.            chain.doFilter(holder.getRequest(), holder.getResponse());  
  31.   
  32.        } finally {  
  33.            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();  
  34.            // Crucial removal of SecurityContextHolder contents - do this before anything else.  
  35.            SecurityContextHolder.clearContext();  
  36.            repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());  
  37.            request.removeAttribute(FILTER_APPLIED);  
  38.   
  39.            if (debug) {  
  40.                logger.debug("SecurityContextHolder now cleared, as request processing completed");  
  41.            }  
  42.        }  
  43.    }  
 

    从代码看: 在一次request中只执行一次,并生成 SecurityContext(从session中读取,如果session中没有就创建一个新的),注册到 SecurityContextHolder中,当请求执行完后,清除该SecurityContext 和request中的 filter_applied 属性。在源码中类注释提到: 这个类 一次请求中只能执行一次,并且它应该在 任何认证过程之前 执行。

   ============================华丽丽的分割线===========================

2,org.springframework.security.web.authentication.logout.LogoutFilter

Java代码   收藏代码
  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)  
  2.             throws IOException, ServletException {  
  3.         HttpServletRequest request = (HttpServletRequest) req;  
  4.         HttpServletResponse response = (HttpServletResponse) res;  
  5.   
  6.         if (requiresLogout(request, response)) {  
  7.             Authentication auth = SecurityContextHolder.getContext().getAuthentication();  
  8.   
  9.             if (logger.isDebugEnabled()) {  
  10.                 logger.debug("Logging out user '" + auth + "' and transferring to logout destination");  
  11.             }  
  12.   
  13.             for (LogoutHandler handler : handlers) {  
  14.                 handler.logout(request, response, auth);  
  15.             }  
  16.   
  17.             logoutSuccessHandler.onLogoutSuccess(request, response, auth);  
  18.   
  19.             return;  
  20.         }  
  21.   
  22.         chain.doFilter(request, response);  
  23.     }  
  24.   
  25.     /** 
  26.      * Allow subclasses to modify when a logout should take place. 
  27.      * 
  28.      * @param request the request 
  29.      * @param response the response 
  30.      * 
  31.      * @return <code>true</code> if logout should occur, <code>false</code> otherwise 
  32.      */  
  33.     protected boolean requiresLogout(HttpServletRequest request, HttpServletResponse response) {  
  34.         String uri = request.getRequestURI();  
  35.         int pathParamIndex = uri.indexOf(';');  
  36.   
  37.         if (pathParamIndex > 0) {  
  38.             // strip everything from the first semi-colon  
  39.             uri = uri.substring(0, pathParamIndex);  
  40.         }  
  41.   
  42.         int queryParamIndex = uri.indexOf('?');  
  43.   
  44.         if (queryParamIndex > 0) {  
  45.             // strip everything from the first question mark  
  46.             uri = uri.substring(0, queryParamIndex);  
  47.         }  
  48.   
  49.         if ("".equals(request.getContextPath())) {  
  50.             return uri.endsWith(filterProcessesUrl);  
  51.         }  
  52.   
  53.         return uri.endsWith(request.getContextPath() + filterProcessesUrl);  
  54.     }  

    这个处理比较简单, 只是检查是否为 登出地址,是的话就退出然后返回,不是的话就进行下一个filter。

   这个判断是否为登录地址我感觉很不正常,他判断是否以 logout_url 结尾,直接判断是否相等才对啊。

  而且他根据 contextpath 是否为“” ,其实不用的,直接判断 contextPath+logout_url 即可。

 

Java代码   收藏代码
  1. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter .java  

 

Java代码   收藏代码
  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)  
  2.             throws IOException, ServletException {  
  3.   
  4.         HttpServletRequest request = (HttpServletRequest) req;  
  5.         HttpServletResponse response = (HttpServletResponse) res;  
  6.   
  7.         if (!requiresAuthentication(request, response)) {  
  8.             chain.doFilter(request, response);  
  9.   
  10.             return;  
  11.         }  
  12.   
  13.         if (logger.isDebugEnabled()) {  
  14.             logger.debug("Request is to process authentication");  
  15.         }  
  16.   
  17.         Authentication authResult;  
  18.   
  19.         try {  
  20.             authResult = attemptAuthentication(request, response);  
  21.             if (authResult == null) {  
  22.                 // return immediately as subclass has indicated that it hasn't completed authentication  
  23.                 return;  
  24.             }  
  25.             sessionStrategy.onAuthentication(authResult, request, response);  
  26.         }  
  27.         catch (AuthenticationException failed) {  
  28.             // Authentication failed  
  29.             unsuccessfulAuthentication(request, response, failed);  
  30.   
  31.             return;  
  32.         }  
  33.   
  34.         // Authentication success  
  35.         if (continueChainBeforeSuccessfulAuthentication) {  
  36.             chain.doFilter(request, response);  
  37.         }  
  38.   
  39.         successfulAuthentication(request, response, authResult);  
  40.     }  
  41.  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {  
  42.         if (postOnly && !request.getMethod().equals("POST")) {  
  43.             throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());  
  44.         }  
  45.   
  46.         String username = obtainUsername(request);  
  47.         String password = obtainPassword(request);  
  48.   
  49.         if (username == null) {  
  50.             username = "";  
  51.         }  
  52.   
  53.         if (password == null) {  
  54.             password = "";  
  55.         }  
  56.   
  57.         username = username.trim();  
  58.   
  59.         UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);  
  60.   
  61.         // Place the last username attempted into HttpSession for views  
  62.         HttpSession session = request.getSession(false);  
  63.   
  64.         if (session != null || getAllowSessionCreation()) {  
  65.             request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));  
  66.         }  
  67.   
  68.         // Allow subclasses to set the "details" property  
  69.         setDetails(request, authRequest);  
  70.   
  71.         return this.getAuthenticationManager().authenticate(authRequest);  
  72.     }  
Java代码   收藏代码
  1. public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {  
  2.     Class<? extends Authentication> toTest = authentication.getClass();  
  3.     AuthenticationException lastException = null;  
  4.     Authentication result = null;  
  5.   
  6.     for (AuthenticationProvider provider : getProviders()) {  
  7.         if (!provider.supports(toTest)) {  
  8.             continue;  
  9.         }  
  10.   
  11.         logger.debug("Authentication attempt using " + provider.getClass().getName());  
  12.   
  13.         try {  
  14.             result = provider.authenticate(authentication);  
  15.   
  16.             if (result != null) {  
  17.                 copyDetails(authentication, result);  
  18.                 break;  
  19.             }  
  20.         } catch (AccountStatusException e) {  
  21.             // SEC-546: Avoid polling additional providers if auth failure is due to invalid account status  
  22.             eventPublisher.publishAuthenticationFailure(e, authentication);  
  23.             throw e;  
  24.         } catch (AuthenticationException e) {  
  25.             lastException = e;  
  26.         }  
  27.     }  
  28.   
  29.     if (result == null && parent != null) {  
  30.         // Allow the parent to try.  
  31.         try {  
  32.             result = parent.authenticate(authentication);  
  33.         } catch (ProviderNotFoundException e) {  
  34.             // ignore as we will throw below if no other exception occurred prior to calling parent and the parent  
  35.             // may throw ProviderNotFound even though a provider in the child already handled the request  
  36.         } catch (AuthenticationException e) {  
  37.             lastException = e;  
  38.         }  
  39.     }  
  40.   
  41.     if (result != null) {  
  42.         if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {  
  43.             // Authentication is complete. Remove credentials and other secret data from authentication  
  44.             ((CredentialsContainer)result).eraseCredentials();  
  45.         }  
  46.   
  47.         eventPublisher.publishAuthenticationSuccess(result);  
  48.         return result;  
  49.     }  
  50.   
  51.     // Parent was null, or didn't authenticate (or throw an exception).  
  52.   
  53.     if (lastException == null) {  
  54.         lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",  
  55.                     new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));  
  56.     }  
  57.   
  58.     eventPublisher.publishAuthenticationFailure(lastException, authentication);  
  59.   
  60.     throw lastException;  
  61. }  
 

   过程: 判断是否 为登录地址,是则进行认证,否则 继续下一个filter

   认证过程: 取得 username,password, 调用 AuthenticationManager.authenticate(){
             然后调用 所有的AuthenticationProvider 进行认证,有一个认证通过即可通过。在AuthenticationProvider中调用 配置的 UserDetailsService 的 loadUserByUserame() 得到 UserDetails,  当第一次从数据库取得后,会将UserDetails保存到 Cache中,这给权限分配的 及时性带来了困难,不过它专门提供了一个filter来进行 热部署权限

      
    }
    还有一点,这个filter中判断 "j_spring_security_check"这个地址也是以 endWith来匹配的,感觉不对。

   回家了,晚上继续

 

===========================华丽丽的分割线=================================

 

org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter

org.springframework.security.web.authentication.www.BasicAuthenticationFilter

org.springframework.security.web.savedrequest.RequestCacheAwareFilter

org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter

org.springframework.security.web.authentication.AnonymousAuthenticationFilter

org.springframework.security.web.session.SessionManagementFilter

org.springframework.security.web.access.ExceptionTranslationFilter

org.springframework.security.web.access.intercept.FilterSecurityInterceptor

 

 

 

分享到:
评论

相关推荐

    Spring Security实现用户登录.docx

    在集成 Spring Security 安全框架的时候我们最先处理的可能就是根据我们项目的实际需要来定制注册登录了,尤其是 Http 登录认证。根据以前的相关文章介绍, Http 登录...今天我们就简单分析它的源码和工作流程。

    JAVA上百实例源码以及开源项目源代码

    Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来...

    JAVA上百实例源码以及开源项目

    笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!此时此...

    java开源包8

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包1

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包11

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包2

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包3

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包6

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包5

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包10

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包4

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包7

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包9

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    java开源包101

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

    Java资源包01

    Spring4GWT ...JARP是为petri 网提供的一个Java编辑器,基于ARP分析器。可以将网络图导出为 GIF, JPEG, PNG, PPM, ARP and PNML (XML based)文件格式。使用了优秀的JHotDraw 5.2 框架。 activemq...

Global site tag (gtag.js) - Google Analytics