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; } }