`

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是授权访问管理器,默认实现由三种
 

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

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

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的实现,最简单的有RoleVoterRoleVoter通过将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;
    }
    

 

 

 

分享到:
评论

相关推荐

    springsecurity3.0.5

    springsecurity3.0.5简单例子 当用firefox访问时产生的临时cookie,在下次访问中还存在的话,获取的session还是原来的session(user验证信息不变),如果把那个临时cookie删除的话,则springsecuriyt生成新的session...

    Spring Security实现用户登录.docx

    在集成 Spring Security 安全框架的时候我们最先处理的可能就是根据我们项目的实际需要来定制注册登录了,尤其是 Http 登录认证。...我们只有把这个过滤器搞清楚才能做一些...今天我们就简单分析它的源码和工作流程。

    Spring Security入门

    本文介绍了Spring Security命名空间配置的核心概念,并说明了Web应用程序中基于表单的简单身份验证所需的设置。

    Apache_Shiro开发文档.zip_barnc7y_java security_laidml2_outerha9_shir

    当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时 可能并不需要那么复杂的东西,所以使用小而简单的Shiro 就足够了。对于它俩到底哪个 好,这个不必纠结,能更简单的解决项目...

    跟A我B学Shiro.zip

    当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时 可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个 好,这个不必纠结,能更简单的解决...

    Java安全框架Shiro电子书

    当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时 可能并不需要那么复杂的东西,所以使用小而简单的Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目...

    开涛Shiro教程.

    目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底...

    apache shrio 示例代码

    目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底...

    Shiro详细教程

    目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底...

    java 安全框架Shiro,pdf版

    当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时 可能并不需要那么复杂的东西,所以使用小而简单的Shiro 就足够了。对于它俩到底哪个 好,这个不必纠结,能更简单的解决项目...

    基于shrio实现的一个权限管理系统

    目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底...

    跟我学Shiro

    当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时 可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个 好,这个不必纠结,能更简单的解决...

    springboot和shiro的结合百知网

    目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part3

    而Spring Boot 是Spring 主推的基于“习惯优于配置”的原则,让你能够快速搭建应用的框架,从而使得Java EE 开发变得异常简单。 《JavaEE开发的颠覆者: Spring Boot实战》从Spring 基础、Spring MVC 基础讲起,从而...

    Spring Boot带前后端 渐进式开发企业级博客系统

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架, 在企业级应用中被广泛使用。本章节不会对该框架做深入探讨,仅从基于角色的权限管理角度,来实现对系统的权限...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part2

    而Spring Boot 是Spring 主推的基于“习惯优于配置”的原则,让你能够快速搭建应用的框架,从而使得Java EE 开发变得异常简单。 《JavaEE开发的颠覆者: Spring Boot实战》从Spring 基础、Spring MVC 基础讲起,从而...

    Shiro开发文档

    当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时 可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个 好,这个不必纠结,能更简单的解决...

    2021年计算机专业|Java毕业设计--在线考试(springboot+vue)

    该在线考试系统毕业设计使用了spring MVC+spring+mybatis+Spring Security+maven技术,提供了随机练习、强化练习、错题管理、 模拟考试、统计分析等功能,可以通过它快捷方便的创建试题和题库,发布试卷,组织考试,...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part1

    而Spring Boot 是Spring 主推的基于“习惯优于配置”的原则,让你能够快速搭建应用的框架,从而使得Java EE 开发变得异常简单。 《JavaEE开发的颠覆者: Spring Boot实战》从Spring 基础、Spring MVC 基础讲起,从而...

    跟我学 Shiro - v1.1.rar

    ring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所 以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。 ...

Global site tag (gtag.js) - Google Analytics