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

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 可否直接省略登录时候的验证

相关推荐

    springsecurity-with-boot-authority:Spring Security中的使用权限样本

    SpringSecurity-with-SpringBoot 使用Spring Security进行表单身份验证。根据权限而不是角色进行检查。 应用程式运动 访问本地主机:8080 / rbac / 以初始用户身份登录 管理员可以更改与角色关联的权限 再次登录时...

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

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

    Spring security用户URL权限FilterSecurityInterceptor使用解析

    主要介绍了Spring security用户URL权限FilterSecurityInterceptor使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    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 ...

    Spring Security 文档

    那么在Spring Security3的使用中,有4种方法: 一种是全部利用配置文件,将用户、权限、资源(url)硬编码在xml文件中,已经实现过,并经过验证; 二种是用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码...

    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 -...

    Spring Security3的使用

    Spring Security3的使用方法有4种: 一种是全部利用配置文件,将用户、权限、资源(url)硬编码在xml文件中。 二种是用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码配置。 三种是细分角色和权限,并将...

    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.0.1文档(翻译版

    一种是全部利用配置文件,将用户、权限、...三种是细分角色和权限,并将用户、角色、权限和资源均采用数据库存储,并且自定义过滤器,代替原有的FilterSecurityInterceptor过滤器,并分别实现AccessDecisionManager、

    word源码java-wordladder3-user-authenticating:wordladder使用sping对用户进行身份验证

    word源码java Word Ladder with authenticating user 1. 实现spring security的四种方法 ...security源码 修改InvocationSecurityMetadataSourceService和UserDetailsService两个类。 前者是将配置文件或数

    项目集成Spring Security.docx

    Security 有两种认证方式: ...formLogin 默认的,如上边那种方式 ...图中橙色的 FilterSecurityInterceptor 是最终的过滤器,它会决定当前的请求可不可以访问Controller,判断规则放在这个里面。

    对接openai接口,采用现有的开源前端实现ai对话(Java)

    FilterSecurityInterceptor 中的doFilter-&gt;invoke方法 //看有没有权限 获取遍历 antMatchers中的url有的话标记为不用授权 AbstractSecurityInterceptor 中Collection&lt;ConfigAttribute&gt; attributes = this....

    Acegi入门到熟练

    Acegi认证授权主要基于两大技术...通过FilterSecurityInterceptor很好地实现 了对URI的保护,通过MethodSecurityInterceptor实现了对Service的方法的拦截保护,通过ACL 实现了对prototype类型的Object进行过滤和保护。

Global site tag (gtag.js) - Google Analytics