zoukankan      html  css  js  c++  java
  • Springsecurity源码注解权限原理(二十)

    使用方式参考:https://www.cnblogs.com/LQBlog/p/15505361.html#autoid-0-0-0

    使用注解权限需要通过以下方式启用

    @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)Import实现原理和使用方式可以参考https://www.cnblogs.com/LQBlog/p/15410425.html

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import({ GlobalMethodSecuritySelector.class })
    @EnableGlobalAuthentication
    @Configuration
    public @interface EnableGlobalMethodSecurity {
    
       
    
    }

    GlobalMethodSecuritySelector

        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            Class<EnableGlobalMethodSecurity> annoType = EnableGlobalMethodSecurity.class;
            Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(annoType.getName(),
                    false);
            //获得注解配置的元数据
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationAttributes);
            Class<?> importingClass = ClassUtils.resolveClassName(importingClassMetadata.getClassName(),
                    ClassUtils.getDefaultClassLoader());
            /**
             * 打了注解的EnableGlobalMethodSecurity是否是GlobalMethodSecurityConfiguration子类
             * 很有用 可以通过继承方式 自定义
             */
            boolean skipMethodSecurityConfiguration = GlobalMethodSecurityConfiguration.class
                    .isAssignableFrom(importingClass);
            AdviceMode mode = attributes.getEnum("mode");
            //proxy类型默认是PROXY
            boolean isProxy = AdviceMode.PROXY == mode;
            String autoProxyClassName = isProxy ? AutoProxyRegistrar.class.getName()
                    : GlobalMethodSecurityAspectJAutoProxyRegistrar.class.getName();
            boolean jsr250Enabled = attributes.getBoolean("jsr250Enabled");
            List<String> classNames = new ArrayList<>(4);
            if (isProxy) {
                //<1>加入MethodSecurityMetadataSourceAdvisorRegistrar 主要是初始化AOPAdvisor
                classNames.add(MethodSecurityMetadataSourceAdvisorRegistrar.class.getName());
            }
            classNames.add(autoProxyClassName);
            //如果当前注解的类不是GlobalMethodSecurityConfiguration子类 则默认初始化GlobalMethodSecurityConfiguration
            if (!skipMethodSecurityConfiguration) {
                //<3>MethodInterceptor 初始化到容器 AOP
                classNames.add(GlobalMethodSecurityConfiguration.class.getName());
            }
            if (jsr250Enabled) {
                //jsr250支持的配置
                classNames.add(Jsr250MetadataSourceConfiguration.class.getName());
            }
            return classNames.toArray(new String[0]);
        }

    <1>

    MethodSecurityMetadataSourceAdvisor就是spring AOP切面 详情可以阅读https://www.cnblogs.com/LQBlog/p/13954302.html#autoid-13-1-0

    /**
     * 采用ImportBeanDefinitionRegistrar 方式导入类参考
     * https://www.cnblogs.com/LQBlog/p/15410425.html#autoid-3-1-0
     */
    class MethodSecurityMetadataSourceAdvisorRegistrar implements ImportBeanDefinitionRegistrar {
    
        /**
         * Register, escalate, and configure the AspectJ auto proxy creator based on the value
         * of the @{@link EnableGlobalMethodSecurity#proxyTargetClass()} attribute on the
         * importing {@code @Configuration} class.
         */
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //创建MethodSecurityMetadataSourceAdvisor BeanDefinition的构造器
            BeanDefinitionBuilder advisor = BeanDefinitionBuilder
                    .rootBeanDefinition(MethodSecurityMetadataSourceAdvisor.class);
            advisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            /**
             * 调用三个参数的构造函数
             * addConstructorArgValue是静态注入
             * addConstructorArgReference 从容器查找注入
             */
            advisor.addConstructorArgValue("methodSecurityInterceptor");//初始化处<3>
            advisor.addConstructorArgReference("methodSecurityMetadataSource");//初始化处<5>
            advisor.addConstructorArgValue("methodSecurityMetadataSource");
            MultiValueMap<String, Object> attributes = importingClassMetadata
                    .getAllAnnotationAttributes(EnableGlobalMethodSecurity.class.getName());
            Integer order = (Integer) attributes.getFirst("order");
            if (order != null) {
                advisor.addPropertyValue("order", order);
            }
            //交给容器初始化<2>
            registry.registerBeanDefinition("metaDataSourceAdvisor", advisor.getBeanDefinition());
        }
    
    }

    <2>

    org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor

    public class MethodSecurityMetadataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
    
            private transient MethodSecurityMetadataSource attributeSource;
    
            private transient MethodInterceptor interceptor;
    
            //织入点 内部类
            private final Pointcut pointcut = new MethodSecurityMetadataSourcePointcut();
    
            private BeanFactory beanFactory;
    
            private final String adviceBeanName;
    
            private final String metadataSourceBeanName;
    
            private transient volatile Object adviceMonitor = new Object();
    
            public MethodSecurityMetadataSourceAdvisor(String adviceBeanName, MethodSecurityMetadataSource attributeSource,
                                                       String attributeSourceBeanName) {
    
                //切入点为methodSecurityInterceptor 由GlobalMethodSecurityConfiguration初始化
                this.adviceBeanName = adviceBeanName;
                //容器注入 methodSecurityMetadataSource由GlobalMethodSecurityConfiguration初始化
                this.attributeSource = attributeSource;
                //值为methodSecurityMetadataSource
                this.metadataSourceBeanName = attributeSourceBeanName;
            }
    
            @Override
            public Pointcut getPointcut() {
                return this.pointcut;
            }
    
            @Override
            public Advice getAdvice() {
                synchronized (this.adviceMonitor) {
                    if (this.interceptor == null) {
                        //容容器获取interceptor methodSecurityInterceptor也就是我们切入的逻辑 由GlobalMethodSecurityConfiguration初始化 参考<3>
                        this.interceptor = this.beanFactory.getBean(this.adviceBeanName, MethodInterceptor.class);
                    }
                    return this.interceptor;
                }
            }
    
            @Override
            public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
                this.beanFactory = beanFactory;
            }
    
            private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
                ois.defaultReadObject();
                this.adviceMonitor = new Object();
                //methodSecurityMetadataSource的实例 由 GlobalMethodSecurityConfiguration初始化
                this.attributeSource = this.beanFactory.getBean(this.metadataSourceBeanName,
                        MethodSecurityMetadataSource.class);
            }
    
            class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
    
                @Override
                public boolean matches(Method m, Class<?> targetClass) {
                    MethodSecurityMetadataSource source =MethodSecurityMetadataSourceAdvisor.this.attributeSource;
                    //<6>匹配是否置入 可以理解为判断方法是否了打了指定注解 如果打了就植入 MethodSecurityMetadataSource 主要就是判断是否打了指定注解 同时保存到map方便后续快速查找
                    return !CollectionUtils.isEmpty(source.getAttributes(m, targetClass));
                }
    
            }
    
        }

    <3>

    如果我们想自定义注解 就看以下类就行

    org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration#methodSecurityInterceptor

        @Bean
        public MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {
            //是否是isAspectJ
            this.methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor()
                    : new MethodSecurityInterceptor();
            //<4>各个授权注解的处理类
            this.methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
            this.methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager());
            //<5>初始化methodSecurityMetadataSource 主要管理我们打了授权注解的元素聚
            this.methodSecurityInterceptor.setSecurityMetadataSource(methodSecurityMetadataSource);
            RunAsManager runAsManager = runAsManager();
            if (runAsManager != null) {
                this.methodSecurityInterceptor.setRunAsManager(runAsManager);
            }
            return this.methodSecurityInterceptor;
        }

    <4>

    org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration#accessDecisionManager

      protected AccessDecisionManager accessDecisionManager() {
            List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
            //针对启用prePostEnabled 增加此注解的Voter处理类
            if (prePostEnabled()) {
                ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
                expressionAdvice.setExpressionHandler(getExpressionHandler());
                decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
            }
            //jsr250Enable 增加此对应注解的处理类
            if (jsr250Enabled()) {
                decisionVoters.add(new Jsr250Voter());
            }
            RoleVoter roleVoter = new RoleVoter();
            GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
            if (grantedAuthorityDefaults != null) {
                roleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
            }
            decisionVoters.add(roleVoter);
            decisionVoters.add(new AuthenticatedVoter());
            //通过AffirmativeBased 管理 内部只是负责遍历匹配
            return new AffirmativeBased(decisionVoters);
        }

    <5> 

    org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration#methodSecurityMetadataSource

        @Bean
        public MethodSecurityMetadataSource methodSecurityMetadataSource() {
            List<MethodSecurityMetadataSource> sources = new ArrayList<>();
            ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(
                    getExpressionHandler());
            MethodSecurityMetadataSource customMethodSecurityMetadataSource = customMethodSecurityMetadataSource();
            if (customMethodSecurityMetadataSource != null) {
                sources.add(customMethodSecurityMetadataSource);
            }
            boolean hasCustom = customMethodSecurityMetadataSource != null;
            //各个注解的支持
            boolean isPrePostEnabled = prePostEnabled();
            boolean isSecuredEnabled = securedEnabled();
            boolean isJsr250Enabled = jsr250Enabled();
            Assert.state(isPrePostEnabled || isSecuredEnabled || isJsr250Enabled || hasCustom,
                    "In the composition of all global method configuration, "
                            + "no annotation support was actually activated");
            //增加对应注解的 解析器,主要是针对自己的注解解析封装到attribute 然后交给自己对应的voter处理
            if (isPrePostEnabled) {
                sources.add(new PrePostAnnotationSecurityMetadataSource(attributeFactory));
            }
            if (isSecuredEnabled) {
                sources.add(new SecuredAnnotationSecurityMetadataSource());
            }
            if (isJsr250Enabled) {
                GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
                Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource = this.context
                        .getBean(Jsr250MethodSecurityMetadataSource.class);
                if (grantedAuthorityDefaults != null) {
                    jsr250MethodSecurityMetadataSource.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
                }
                sources.add(jsr250MethodSecurityMetadataSource);
            }
    //<6>通过Deletgating封装 统一管理 并提供缓存<6>
    return new DelegatingMethodSecurityMetadataSource(sources); }

    <6>

    org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource#getAttributes

     @Override
        public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
            //方法和class作为cacheKey
            DelegatingMethodSecurityMetadataSource.DefaultCacheKey cacheKey = new DelegatingMethodSecurityMetadataSource.DefaultCacheKey(method, targetClass);
            //同步锁
            synchronized (this.attributeCache) {
                //先判断cache是否有
                Collection<ConfigAttribute> cached = this.attributeCache.get(cacheKey);
                // 如果有直接返回
                if (cached != null) {
                    return cached;
                }
                //遍历MethodSecurityMetadataSource 解析注解获得ConfigAttribute
                Collection<ConfigAttribute> attributes = null;
                for (MethodSecurityMetadataSource s : this.methodSecurityMetadataSources) {
                    attributes = s.getAttributes(method, targetClass);
                    if (attributes != null && !attributes.isEmpty()) {
                        break;
                    }
                }
                // Put it in the cache.
                if (attributes == null || attributes.isEmpty()) {
                    this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
                    return NULL_CONFIG_ATTRIBUTE;
                }
                this.attributeCache.put(cacheKey, attributes);
                return attributes;
            }
        }
  • 相关阅读:
    法里数列
    母函数笔记
    贝尔数的指数母函数推导
    jQuery监听文本框值改变触发事件(propertychange)
    java-->TreeMap的使用
    查找-->二分查找和插值查找java实现
    查找-->斐波那契查找算法
    排序-->归并排序
    希尔排序(交换法和位移法)
    8皇后算法的简单实现(回溯)
  • 原文地址:https://www.cnblogs.com/LQBlog/p/15549121.html
Copyright © 2011-2022 走看看