Spring-security原始碼-許可權之註解許可權原理(十八)
阿新 • • 發佈:2021-11-13
使用方式參考: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); }