1. 程式人生 > 其它 >Spring-security原始碼-許可權之註解許可權原理(十八)

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

final class GlobalMethodSecuritySelector implements ImportSelector {

    @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()); 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); 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");
        advisor.addConstructorArgReference("methodSecurityMetadataSource");
        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>

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初始化
                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;
            //匹配是否植入 可以理解為判斷方法是否了打了指定註解 如果打了就植入
            return !CollectionUtils.isEmpty(source.getAttributes(m, targetClass));
        }

    }

}

<3>

如果我們想自定義註解 就看以下類就行

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

@Bean
    public MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {
        this.methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor()
                : new MethodSecurityInterceptor();
        this.methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
        this.methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager());
        this.methodSecurityInterceptor.setSecurityMetadataSource(methodSecurityMetadataSource);
        RunAsManager runAsManager = runAsManager();
        if (runAsManager != null) {
            this.methodSecurityInterceptor.setRunAsManager(runAsManager);
        }
        return this.methodSecurityInterceptor;
    }
@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");
        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);
        }
        return new DelegatingMethodSecurityMetadataSource(sources);
    }