spring security 简单分析

1:spring security 对于普通的URL权限验证基于过滤器实现的

在web.xml 文件中这样配置

<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

其中 filter-name一定要配成springSecurityFilterChain,因为DelegatingFilterProxy初始化的时候回根据配置的filter-name 从容器中查找对因的spring security  统一处理过滤器 返回的是FilterChainProxy 。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        FilterInvocation fi = new FilterInvocation(request, response, chain);
       //根据请求的URL 放回相应的过滤器
        List filters = getFilters(fi.getRequestUrl());

        if (filters == null || filters.size() == 0) {
            if (logger.isDebugEnabled()) {
                logger.debug(fi.getRequestUrl() +
                        filters == null ? " has no matching filters" : " has an empty filter list");
            }

            chain.doFilter(request, response);

            return;
        }
        //构建spring security 过滤器链对象
        VirtualFilterChain virtualFilterChain = new VirtualFilterChain(fi, filters);
        virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
    }

  根据Url返回过滤器其中会包含一个FilterSecurityInterceptor过滤器,这个是专门处理http url 权限认证的过滤器,下面是他的doFilter

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        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) {
            // filter already applied to this request and user wants us to observce
            // once-per-request handling, so don't re-do security checking
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } else {
            // first time this request being called, so perform security checking
            if (fi.getRequest() != null) {
                fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
            }
           //这个方法是权限验证的入口调用父类的beforeInvocation方法
            InterceptorStatusToken token = super.beforeInvocation(fi);

            try {
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            } finally {
                super.afterInvocation(token, null);
            }
        }
    }

 在上面我们看到invoke 方法会调用父类的beforeInvocation方法。父类是AbstractSecurityInterceptor,这个类是权限验证的主类,

包含子类有

  • EndpointInterceptor
  • FilterSecurityInterceptor    对应http资源过滤
  • MethodSecurityInterceptor     对应spring aop 拦截过滤
  • AspectJSecurityInterceptor 
  • AspectJAnnotationSecurityInterceptor
protected InterceptorStatusToken beforeInvocation(Object object) {
        Assert.notNull(object, "Object was null");

        if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
            throw new IllegalArgumentException("Security invocation attempted for object "
                    + object.getClass().getName()
                    + " but AbstractSecurityInterceptor only configured to support secure objects of type: "
                    + getSecureObjectClass());
        }
       //获取资源对应的权限配置
        ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);

        if (attr == null) {
            if (rejectPublicInvocations) {
                throw new IllegalArgumentException(
                        "No public invocations are allowed via this AbstractSecurityInterceptor. "
                                + "This indicates a configuration error because the "
                                + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");
            }

            if (logger.isDebugEnabled()) {
                logger.debug("Public object - authentication not attempted");
            }

            publishEvent(new PublicInvocationEvent(object));

            return null; // no further work post-invocation
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Secure object: " + object + "; ConfigAttributes: " + attr);
        }

        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
                    "An Authentication object was not found in the SecurityContext"), object, attr);
        }
        //判断每次请求是否都需要进行认证检查
        Authentication authenticated = authenticateIfRequired();

        
        try {
             //权限验证
            this.accessDecisionManager.decide(authenticated, object, attr);
        }
        catch (AccessDeniedException accessDeniedException) {
            AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,
                    accessDeniedException);
            publishEvent(event);

            throw accessDeniedException;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Authorization successful");
        }

        AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);
        publishEvent(event);

        // Attempt to run as a different user
        Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);

        if (runAs == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("RunAsManager did not change Authentication object");
            }

            // no further work post-invocation
            return new InterceptorStatusToken(authenticated, false, attr, object);
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Switching to RunAs Authentication: " + runAs);
            }

            SecurityContextHolder.getContext().setAuthentication(runAs);

            // revert to token.Authenticated post-invocation
            return new InterceptorStatusToken(authenticated, true, attr, object);
        }
    }
在上面方法中 this.accessDecisionManager.decide(authenticated, object, attr);才是资源授权的
地方,accessDecisionManager是授权访问管理器,默认实现由三种

 (1)UnanimousBased(全票通过):所有投票器都通过才允许访问资源。

(2)ConsensusBased(少数服从多数):超过一半的投票器通过才允许访问资源。
(3)AffirmativeBased(一票通过):只要有一个投票器投票通过,就允许访问资源。

URL资源授权默认走AffirmativeBased这个授权访问管理器,其授权代码如下

public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
        throws AccessDeniedException {
        //获取当前访问授权管理器的投票器
        Iterator iter = this.getDecisionVoters().iterator();
        int deny = 0;

        while (iter.hasNext()) {
            AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
            int result = voter.vote(authentication, object, config);

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

            case AccessDecisionVoter.ACCESS_DENIED:
                deny++;

                break;

            default:
                break;
            }
        }

        if (deny > 0) {
            throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
                    "Access is denied"));
        }

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

 

Spring Security中有很多AccessDecisionVoter的实现,最简单的有RoleVoter。RoleVoter通过将Authentication里面获取的权限信息,与从ConfigAttributeDefinition配置的访问资源需要的权限对比,来投票通过,或拒绝,或弃权。代码如下
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
        int result = ACCESS_ABSTAIN;
        Iterator iter = config.getConfigAttributes().iterator();
        GrantedAuthority[] authorities = extractAuthorities(authentication);        

        while (iter.hasNext()) {
            ConfigAttribute attribute = (ConfigAttribute) iter.next();

            if (this.supports(attribute)) {
                result = ACCESS_DENIED;

                // Attempt to find a matching granted authority
                for (int i = 0; i < authorities.length; i++) {
                    if (attribute.getAttribute().equals(authorities[i].getAuthority())) {
                        return ACCESS_GRANTED;
                    }
                }
            }
        }

        return result;
    }

 

相关推荐