Spring-security源码-注解权限原理(二十)

使用方式参考: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;
        }
    }

 

posted @ 2021-11-13 17:12  意犹未尽  阅读(526)  评论(0编辑  收藏  举报