Spring Security 自定义access decision使用SpEL

Spring Security允许通过security命名空间来配置AccessDecisionManager。元素的access-decision-manager-ref属性来指明一个实现了AccessDecisionManager的Spring Bean。Spring Security提供了这个接口的三个实现类,都在org.springframework.security.access.vote包中:

 

类名 描述
AffirmativeBased 如果有任何一个投票器允许访问,请求将被立刻允许,而不管之前可能有的拒绝决定。
ConsensusBased 多数票(允许或拒绝)决定了AccessDecisionManager的结果。平局的投票和空票(全是弃权的)的结果是可配置的。
UnanimousBased 所有的投票器必须全是允许的,否则访问将被拒绝。

 

当Spring Security 上下文使用自动配置的时候,Spring 会自动注册一个 AffirmativeBased 投票管理器,表示只要有一个投票器允许访问,请求将被允许通过。调用过程如下图所示:

 


Spring Security 自定义access decision使用SpEL_第1张图片
 
 观察org.springframework.security.config.http.HttpConfigurationBuilder类的createFilterSecurityInterceptor(BeanReference authManager) 方法代码:

 

private void createFilterSecurityInterceptor(BeanReference authManager) {
        //判断是否配置了use-expressions属性
        boolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(httpElt);
        RootBeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser.createSecurityMetadataSource(interceptUrls, httpElt, pc);

        RootBeanDefinition accessDecisionMgr;
        ManagedList voters =  new ManagedList(2);
        //如果use-expressions=true, 则使用WebExpressionVoter, 否则使用RoleVoter和AuthenticatedVoter
        if (useExpressions) {
            BeanDefinitionBuilder expressionVoter = BeanDefinitionBuilder.rootBeanDefinition(WebExpressionVoter.class);
            // Read the expression handler from the FISMS
            RuntimeBeanReference expressionHandler = (RuntimeBeanReference)
                    securityMds.getConstructorArgumentValues().getArgumentValue(1, RuntimeBeanReference.class).getValue();

            expressionVoter.addPropertyValue("expressionHandler", expressionHandler);

            voters.add(expressionVoter.getBeanDefinition());
        } else {
            voters.add(new RootBeanDefinition(RoleVoter.class));
            voters.add(new RootBeanDefinition(AuthenticatedVoter.class));
        }
        // 初始化默认的AffirmativeBased
        accessDecisionMgr = new RootBeanDefinition(AffirmativeBased.class);
        // 添加投票器
        accessDecisionMgr.getConstructorArgumentValues().addGenericArgumentValue(voters);
        accessDecisionMgr.setSource(pc.extractSource(httpElt));

        // 设置Access Manager
        String accessManagerId = httpElt.getAttribute(ATT_ACCESS_MGR);
        // 如果配置文件没有明确配置的access-decision-manager-ref属性,
        // 默认将上面初始化的AffirmativeBased作为access manager
        if (!StringUtils.hasText(accessManagerId)) {
            accessManagerId = pc.getReaderContext().generateBeanName(accessDecisionMgr);
            pc.registerBeanComponent(new BeanComponentDefinition(accessDecisionMgr, accessManagerId));
        }

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
        // 如果配置文件明确配置的access-decision-manager-ref属性, 则直接添加accessDecisionManager引用该access manager。
        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();
        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);
    }

 如果需要实现替换内置的AffirmativeBased管理器,明确定义access manager,例如启用UnanimousBased 并且在元素中使用SpEL表达式,就必须明确指定使用一个WebExpressionVoter 投票器。见下图:

 

 




    
    
        
        
        
        
        

        
    
    
        
            
                
                
            
        
    

    
        
            
                
                
                
                
            
        
    
    
    
    
这样配置才能顺利使用SpEL表达式。否则,如果不在投票管理器明确指定WebExpressionVoter,将如下错误 Caused by: java.lang.IllegalArgumentException: Unsupported configuration attributes: [hasRole('ROLE_USER'), permitAll, permitAll, permitAll, permitAll] :

 

21:09:01.417 [RMI TCP Connection(2)-127.0.0.1] ERROR o.s.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot resolve reference to bean 'org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0' while setting constructor argument with key [10]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Unsupported configuration attributes: [hasRole('ROLE_USER'), permitAll, permitAll, permitAll, permitAll]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:351) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:154) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1456) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304) ~[AbstractBeanFactory$1.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[DefaultSingletonBeanRegistry.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300) ~[AbstractBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195) ~[AbstractBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:681) ~[DefaultListableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760) ~[AbstractApplicationContext.class:4.0.2.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482) ~[AbstractApplicationContext.class:4.0.2.RELEASE]
	at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403) ~[ContextLoader.class:4.0.2.RELEASE]
	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306) ~[ContextLoader.class:4.0.2.RELEASE]
	at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106) [ContextLoaderListener.class:4.0.2.RELEASE]
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4810) [catalina.jar:8.0.0-RC10]
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5248) [catalina.jar:8.0.0-RC10]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) [catalina.jar:8.0.0-RC10]
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:726) [catalina.jar:8.0.0-RC10]
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:702) [catalina.jar:8.0.0-RC10]
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:699) [catalina.jar:8.0.0-RC10]
	at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1647) [catalina.jar:8.0.0-RC10]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_51]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_51]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_51]
	at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_51]
	at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300) [tomcat-coyote.jar:8.0.0-RC10]
	at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) [na:1.7.0_51]
	at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) [na:1.7.0_51]
	at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:465) [catalina.jar:8.0.0-RC10]
	at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:415) [catalina.jar:8.0.0-RC10]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_51]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_51]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_51]
	at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_51]
	at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300) [tomcat-coyote.jar:8.0.0-RC10]
	at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) [na:1.7.0_51]
	at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) [na:1.7.0_51]
	at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487) [na:1.7.0_51]
	at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97) [na:1.7.0_51]
	at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328) [na:1.7.0_51]
	at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420) [na:1.7.0_51]
	at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848) [na:1.7.0_51]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_51]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_51]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_51]
	at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_51]
	at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322) [na:1.7.0_51]
	at sun.rmi.transport.Transport$1.run(Transport.java:177) [na:1.7.0_51]
	at sun.rmi.transport.Transport$1.run(Transport.java:174) [na:1.7.0_51]
	at java.security.AccessController.doPrivileged(Native Method) [na:1.7.0_51]
	at sun.rmi.transport.Transport.serviceCall(Transport.java:173) [na:1.7.0_51]
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:556) [na:1.7.0_51]
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:811) [na:1.7.0_51]
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:670) [na:1.7.0_51]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_51]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_51]
	at java.lang.Thread.run(Thread.java:744) [na:1.7.0_51]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot resolve reference to bean 'org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0' while setting constructor argument with key [10]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Unsupported configuration attributes: [hasRole('ROLE_USER'), permitAll, permitAll, permitAll, permitAll]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:351) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:154) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:626) ~[ConstructorResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:140) ~[ConstructorResolver.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1114) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1017) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
14-Apr-2014 21:09:01.430 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.StandardContext.startInternal Error listenerStart
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304) ~[AbstractBeanFactory$1.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[DefaultSingletonBeanRegistry.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300) ~[AbstractBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195) ~[AbstractBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:320) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	... 60 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Unsupported configuration attributes: [hasRole('ROLE_USER'), permitAll, permitAll, permitAll, permitAll]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1553) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304) ~[AbstractBeanFactory$1.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[DefaultSingletonBeanRegistry.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300) ~[AbstractBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195) ~[AbstractBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:320) ~[BeanDefinitionValueResolver.class:4.0.2.RELEASE]
	... 74 common frames omitted
Caused by: java.lang.IllegalArgumentException: Unsupported configuration attributes: [hasRole('ROLE_USER'), permitAll, permitAll, permitAll, permitAll]
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.afterPropertiesSet(AbstractSecurityInterceptor.java:156) ~[AbstractSecurityInterceptor.class:3.2.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549) ~[AbstractAutowireCapableBeanFactory.class:4.0.2.RELEASE]
	... 81 common frames omitted
 

你可能感兴趣的:(spring,security)