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

Spring Security3源码分析-FilterSecurityInterceptor分析

 
阅读更多
FilterSecurityInterceptor过滤器对应的类路径为
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
这个filter是filterchain中比较复杂,也是比较核心的过滤器,主要负责授权的工作
在看这个filter源码之前,先来看看spring是如何构造filter这个bean的
具体的构造过程的代码片段为
    //这个方法源自HttpConfigurationBuilder类
    void createFilterSecurityInterceptor(BeanReference authManager) {
        //判断是否配置了use-expressions属性
        boolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(httpElt);
        //根据intercept-url标签列表创建授权需要的元数据信息。后面仔细分析
        BeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser.createSecurityMetadataSource(interceptUrls, httpElt, pc);

        RootBeanDefinition accessDecisionMgr;
        //创建voter列表
        ManagedList<BeanDefinition> voters =  new ManagedList<BeanDefinition>(2);
        //如果是使用了表达式,使用WebExpressionVoter
        //没使用表达式,就使用RoleVoter、AuthenticatedVoter
        if (useExpressions) {
            voters.add(new RootBeanDefinition(WebExpressionVoter.class));
        } else {
            voters.add(new RootBeanDefinition(RoleVoter.class));
            voters.add(new RootBeanDefinition(AuthenticatedVoter.class));
        }
        //定义授权的决策管理类AffirmativeBased
        accessDecisionMgr = new RootBeanDefinition(AffirmativeBased.class);
        //添加依赖的voter列表
        accessDecisionMgr.getPropertyValues().addPropertyValue("decisionVoters", voters);
        accessDecisionMgr.setSource(pc.extractSource(httpElt));

        // Set up the access manager reference for http
        String accessManagerId = httpElt.getAttribute(ATT_ACCESS_MGR);
        //如果未定义access-decision-manager-ref属性,就使用默认的
         //AffirmativeBased
        if (!StringUtils.hasText(accessManagerId)) {
            accessManagerId = pc.getReaderContext().generateBeanName(accessDecisionMgr);
            pc.registerBeanComponent(new BeanComponentDefinition(accessDecisionMgr, accessManagerId));
        }
        //创建FilterSecurityInterceptor过滤器
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
        //添加决策管理器
        builder.addPropertyReference("accessDecisionManager", accessManagerId);
        //添加认证管理类
        builder.addPropertyValue("authenticationManager", authManager);

        if ("false".equals(httpElt.getAttribute(ATT_ONCE_PER_REQUEST))) {
            builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
        }
        //添加授权需要的安全元数据资源
        builder.addPropertyValue("securityMetadataSource", securityMds);
        BeanDefinition fsiBean = builder.getBeanDefinition();
        //向ioc容器注册bean
        String fsiId = pc.getReaderContext().generateBeanName(fsiBean);
        pc.registerBeanComponent(new BeanComponentDefinition(fsiBean,fsiId));

        // Create and register a DefaultWebInvocationPrivilegeEvaluator for use with taglibs etc.
        BeanDefinition wipe = new RootBeanDefinition(DefaultWebInvocationPrivilegeEvaluator.class);
        wipe.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(fsiId));

        pc.registerBeanComponent(new BeanComponentDefinition(wipe, pc.getReaderContext().generateBeanName(wipe)));

        this.fsi = new RuntimeBeanReference(fsiId);
    }

现在再仔细分析创建元数据资源的bean过程
    static BeanDefinition createSecurityMetadataSource(List<Element> interceptUrls, Element elt, ParserContext pc) {
        //创建Url处理类,有两个实现:AntUrlPathMatcher、RegexUrlPathMatcher
        UrlMatcher matcher = HttpSecurityBeanDefinitionParser.createUrlMatcher(elt);
        boolean useExpressions = isUseExpressions(elt);
        //解析intercept-url标签,构造所有需要拦截url的map信息
         //map中的key:RequestKey的bean定义,value:SecurityConfig的bean定义
        ManagedMap<BeanDefinition, BeanDefinition> requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap(
                interceptUrls, useExpressions, pc);
        BeanDefinitionBuilder fidsBuilder;

        if (useExpressions) {
            //定义表达式处理类的bean
            Element expressionHandlerElt = DomUtils.getChildElementByTagName(elt, Elements.EXPRESSION_HANDLER);
            String expressionHandlerRef = expressionHandlerElt == null ? null : expressionHandlerElt.getAttribute("ref");

            if (StringUtils.hasText(expressionHandlerRef)) {
                logger.info("Using bean '" + expressionHandlerRef + "' as web SecurityExpressionHandler implementation");
            } else {
                BeanDefinition expressionHandler = BeanDefinitionBuilder.rootBeanDefinition(DefaultWebSecurityExpressionHandler.class).getBeanDefinition();
                expressionHandlerRef = pc.getReaderContext().generateBeanName(expressionHandler);
                pc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));
            }
            //定义表达式类型的FilterInvocationSecurityMetadataSource
            fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(ExpressionBasedFilterInvocationSecurityMetadataSource.class);
            //通过构造函数注入依赖
            fidsBuilder.addConstructorArgValue(matcher);
            fidsBuilder.addConstructorArgValue(requestToAttributesMap);
            fidsBuilder.addConstructorArgReference(expressionHandlerRef);
        } else {
            //定义非表达式类型的FilterInvocationSecurityMetadataSource
            fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);
            //通过构造函数注入依赖
            fidsBuilder.addConstructorArgValue(matcher);
            fidsBuilder.addConstructorArgValue(requestToAttributesMap);
        }

        fidsBuilder.addPropertyValue("stripQueryStringFromUrls", matcher instanceof AntUrlPathMatcher);
        fidsBuilder.getRawBeanDefinition().setSource(pc.extractSource(elt));

        return fidsBuilder.getBeanDefinition();
    }


通过以上的bean构造过程,FilterSecurityInterceptor所依赖的决策管理器、认证管理器、安全元数据资源都具备了,该让FilterSecurityInterceptor干活了,其源码为
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        //封装request, response, chain,方便参数传递、增加代码阅读性
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
                && observeOncePerRequest) {
            if (fi.getRequest() != null) {
                fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
            }
            //执行父类beforeInvocation,类似于aop中的before
            InterceptorStatusToken token = super.beforeInvocation(fi);

            try {
                //filter传递
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            } finally {
                //执行父类的afterInvocation,类似于aop中的after
                super.afterInvocation(token, null);
            }
        }
    }

继续看父类的beforeInvocation方法,其中省略了一些不重要的代码片段
    protected InterceptorStatusToken beforeInvocation(Object object) {
        //根据SecurityMetadataSource获取配置的权限属性
        Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
        //省略……
         //判断是否需要对认证实体重新认证,默认为否
        Authentication authenticated = authenticateIfRequired();

        // Attempt authorization
        try {
            //决策管理器开始决定是否授权,如果授权失败,直接抛出AccessDeniedException
            this.accessDecisionManager.decide(authenticated, object, attributes);
        }
        catch (AccessDeniedException accessDeniedException) {
            publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
                    accessDeniedException));

            throw accessDeniedException;
        }
    }

增加说明
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);

这里获取的是权限列表信息,比如说有这个配置
<security:intercept-url pattern="/index.jsp*" access="ROLE_USER,ROLE_ADMIN"/>
如果现在发起一个请求时index.jsp,那么根据这个请求返回的attributes集合就是分别包含ROLE_USER,ROLE_ADMIN属性的两个SecurityConfig对象

至于请求url如何匹配的,大家可以通过阅读DefaultFilterInvocationSecurityMetadataSource类的源码,实际上,这里用到了spring的路径匹配工具类org.springframework.util.AntPathMatcher
AntPathMatcher匹配方式的通配符有三种:
    ?(匹配任何单字符),*(匹配0或者任意数量的字符),**(匹配0或者更多的目录)

由于之前在bean的定义过程已经知道决策管理器是AffirmativeBased,接着看AffirmativeBased的决策过程
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException {
        int deny = 0;
        //循环voters,实际上是RoleVoter、AuthenticatedVoter
        for (AccessDecisionVoter voter : getDecisionVoters()) {
            //把具体的决策任务交给voter处理
              //voter只返回-1、0、1,只有为1才算授权成功
            int result = voter.vote(authentication, object, configAttributes);

            if (logger.isDebugEnabled()) {
                logger.debug("Voter: " + voter + ", returned: " + result);
            }

            switch (result) {
            case AccessDecisionVoter.ACCESS_GRANTED:
                return;

            case AccessDecisionVoter.ACCESS_DENIED:
                deny++;

                break;

            default:
                break;
            }
        }
        //只要有一个voter拒绝了,则直接抛出访问拒绝异常
        if (deny > 0) {
            throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
                    "Access is denied"));
        }

        // To get this far, every AccessDecisionVoter abstained
        checkAllowIfAllAbstainDecisions();
    }

实际上,有三种决策管理器,分别为AffirmativeBased、ConsensusBased、UnanimousBased,各自决策的区别是:
AffirmativeBased:只要有一个voter投同意票,就授权成功
ConsensusBased:只要投同意票的大于投反对票的,就授权成功
UnanimousBased:需要一致通过才授权成功
具体决策规则很简单,只是根据voter返回的结果做处理
接下来,分别看RoleVoter、AuthenticatedVoter的源码
RoleVoter:
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        int result = ACCESS_ABSTAIN;
        //从认证实体中获取所有的权限列表
        Collection<GrantedAuthority> authorities = extractAuthorities(authentication);
        //循环intercept-url配置的access权限列表
        for (ConfigAttribute attribute : attributes) {
            if (this.supports(attribute)) {
                result = ACCESS_DENIED;

                // Attempt to find a matching granted authority
                //循环认证实体所拥有的权限列表
                for (GrantedAuthority authority : authorities) {
                    if (attribute.getAttribute().equals(authority.getAuthority())) {
                        //只要有相同的权限,直接返回成功1
                        return ACCESS_GRANTED;
                    }
                }
            }
        }

        return result;
    }

AuthenticatedVoter:
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        int result = ACCESS_ABSTAIN;

        for (ConfigAttribute attribute : attributes) {
            if (this.supports(attribute)) {
                result = ACCESS_DENIED;

                if (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())) {
                    if (isFullyAuthenticated(authentication)) {
                        return ACCESS_GRANTED;
                    }
                }

                if (IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())) {
                    if (authenticationTrustResolver.isRememberMe(authentication)
                        || isFullyAuthenticated(authentication)) {
                        return ACCESS_GRANTED;
                    }
                }

                if (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) {
                    if (authenticationTrustResolver.isAnonymous(authentication) || isFullyAuthenticated(authentication)
                        || authenticationTrustResolver.isRememberMe(authentication)) {
                        return ACCESS_GRANTED;
                    }
                }
            }
        }

        return result;
    }

由于RoleVoter在list列表中的位置处于AuthenticatedVoter前面,只要RoleVoter通过,就不会再执行AuthenticatedVoter了。实际上AuthenticatedVoter只会对IS_AUTHENTICATED_FULLY、IS_AUTHENTICATED_REMEMBERED、IS_AUTHENTICATED_ANONYMOUSLY三种权限做vote处理。
分享到:
评论
1 楼 wangzhiwei231 2015-09-30  
有一个疑问 authenticationManager作为认证管理器 在认证和授权的过程中 作用是不是和UsernamepasswordAutenticationFilter里面的authenticationManager 是一样的  那如果有了filterSecurityInterceptor 可否直接省略登录时候的验证

相关推荐

    spring源码分析(1-10)

    Spring 源代码分析系列涵盖了多个关键模块,包括事务处理、IoC容器、JDBC、MVC、AOP以及与Hibernate和Acegi安全框架的集成。以下是对这些知识点的详细阐述: 1. **Spring 事务处理**:Spring 提供了声明式事务管理...

    spring security源码分析.pdf

    ### Spring Security 源码分析知识...以上内容涵盖了 Spring Security 3 的源码分析中几个关键点的具体内容。通过对这些内容的深入学习和理解,可以更好地掌握 Spring Security 的工作原理及其在实际项目中的应用技巧。

    Spring_Security源码分析

    在这个源码分析中,我们将聚焦于 `ConfigAttributeDefinition` 类,它是Spring Security配置核心组件的一部分。 `ConfigAttributeDefinition` 类扮演着配置属性的容器角色。它用于封装一系列的 `ConfigAttribute` ...

    spring-security3.0.5源码

    3. **Filter Chain(过滤器链)**:Spring Security的核心在于其Web安全过滤器链,如`DelegatingFilterProxy`、`FilterSecurityInterceptor`和`ChannelProcessingFilter`等。这些过滤器在HTTP请求进入应用之前进行...

    spring-security-3.1.x.zip 源码下载

    4. **源码分析**: 通过源码可以深入了解Spring Security的工作流程,例如: - 查看`AbstractAuthenticationProcessingFilter`,了解如何处理登录请求。 - 分析`FilterSecurityInterceptor`,理解授权过程中的...

    Spring Security 源码

    在分析Spring Security源码时,你可能会关注以下关键组件: - `AbstractSecurityInterceptor`:所有安全拦截器的基类。 - `FilterSecurityInterceptor`:处理HTTP请求的安全过滤器。 - `AuthenticationManager` 和 `...

    spring-security-3.0.3 jar包( 含源码)

    源码分析: 在Spring Security 3.0.3的源码中,开发者可以研究以下关键组件: - `AuthenticationProvider`:处理认证请求的接口,实现它可以自定义认证逻辑。 - `FilterSecurityInterceptor`:AOP拦截器,负责进行...

    springsecurity3 学习笔记源码分析所得

    Spring Security 是一个强大的Java安全框架,用于...总的来说,Spring Security 3的学习笔记和源码分析对提升安全开发技能大有裨益,不仅可以加深理论理解,还能在实际项目中灵活运用,构建更加健壮、安全的应用系统。

    Spring-security源代码下载

    同时,源码分析也有助于学习Spring框架的设计原则和最佳实践,比如AOP(面向切面编程)、DI(依赖注入)等。对于想要提升自身安全开发能力的程序员,研究Spring Security源码是一项宝贵的学习任务。

    Spring_Security3_源码分析

    通过这些源码分析,我们可以了解到Spring Security是如何在背后工作,保护应用程序免受未经授权的访问。理解这些组件的工作原理对于定制和优化安全配置至关重要,同时也有助于开发者解决潜在的安全问题。

    Spring Security 权限控制

    **源码分析** 理解 Spring Security 的源码对于深度定制和优化安全策略至关重要。主要关注以下几个关键类: - `AbstractSecurityInterceptor`: 所有安全拦截器的基类,处理认证和授权。 - `...

    Spring Security3 配置使用

    6. **源码分析**:对于深入理解Spring Security的工作原理,阅读源码是十分有帮助的。可以研究`AuthenticationManager`、`FilterSecurityInterceptor`、`AbstractAuthenticationProcessingFilter`等关键类的实现。 ...

    spring-security-3.2.9的jar包和源码包

    7. **源码分析**:源码包允许开发者深入研究Spring Security的实现细节,了解其内部的工作流程,这对于自定义扩展和调试是非常有价值的。例如,可以通过阅读`AuthenticationManager`和`UserDetailsService`的实现来...

    spring security source code

    《深入剖析Spring Security源码与Spring Boot集成》 在当今的Web开发中,安全问题始终是不可忽视的重要环节。Spring Security作为Spring生态系统中的一个强大安全框架,为开发者提供了丰富的功能来保护应用程序。...

    SpringSecurity3框架

    源码分析对于深入理解Spring Security的工作原理至关重要。通过阅读源码,开发者可以了解每个组件如何协同工作,以及如何根据特定需求定制框架。例如,你可以研究`FilterSecurityInterceptor`如何调用`...

    spring security简单示例

    6. **源码分析**: Spring Security的源码是开源的,你可以深入研究其内部工作原理,了解每个组件是如何协同工作的。这将帮助你更好地理解和定制这个框架。 7. **工具**: - Spring Security的官方文档和教程是...

    spring-security 3.02 jar包及源码.rar

    源码分析可以帮助我们深入理解Spring Security的工作原理,包括它如何处理认证、授权、过滤请求、会话管理等。通过对源码的学习,开发者可以更有效地定制和优化Spring Security以满足特定的应用场景需求。例如,你...

    spring security 源文件

    本篇文章将深入探讨Spring Security的核心概念、架构及其源码分析。 1. **核心组件** - **Filter Chain**: Spring Security 的核心是过滤器链,它处理HTTP请求,执行安全相关的操作,如认证和授权。 - **...

    spring-security-3.0.7.RELEASE.zip

    本篇文章将详细探讨Spring Security 3.0.7.RELEASE版本的核心特性和实际应用场景,通过源码分析,帮助开发者更深入地理解和掌握这一强大的安全框架。 一、Spring Security概述 Spring Security旨在提供认证...

    Spring Security认证权限管理

    **源码分析** 深入理解Spring Security需要研究其源码。核心类如`AbstractAuthenticationProcessingFilter`处理认证,`FilterSecurityInterceptor`处理授权,`AbstractAccessDecisionManager`管理决策过程。理解...

Global site tag (gtag.js) - Google Analytics