1. 程式人生 > >spring aop(十)--spring security啟用全域性方法使用aop的分析

spring aop(十)--spring security啟用全域性方法使用aop的分析

使用以下配置就可啟用spring security全域性方法:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

}

它大致是怎樣起作用的?

先從EnableGlobalMethodSecurity入手

@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value={java.lang.annotation.ElementType.TYPE})
@Documented
@Import({GlobalMethodSecuritySelector.class,ObjectPostProcessorConfiguration.class})
@EnableGlobalAuthentication
public @interface EnableGlobalMethodSecurity {
    boolean prePostEnabled() default false;
    boolean securedEnabled() default false;
    boolean jsr250Enabled() default false;
    //設定是否使用CGLib建立代理
    boolean proxyTargetClass() default false;
    AdviceMode mode() default AdviceMode.PROXY;
    int order() default Ordered.LOWEST_PRECEDENCE;
}

@EnableGlobalAuthentication是與認證有關的配置,不做分析.ObjectPostProcessorConfiguration也不是分析關心的配置,重點看看GlobalMethodSecuritySelector.

final class GlobalMethodSecuritySelector implements ImportSelector {
    public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Class<EnableGlobalMethodSecurity> annoType = EnableGlobalMethodSecurity.class;
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(annoType.getName(), false);
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationAttributes);
        Assert.notNull(attributes, String.format(
                "@%s is not present on importing class '%s' as expected",
                annoType.getSimpleName(), importingClassMetadata.getClassName()));
        // TODO would be nice if could use BeanClassLoaderAware (does not work)
        Class<?> importingClass = ClassUtils.resolveClassName(importingClassMetadata.getClassName(), ClassUtils.getDefaultClassLoader());
	//因為MethodSecurityConfig繼承了GlobalMethodSecurityConfiguration,所以為true
        boolean skipMethodSecurityConfiguration = GlobalMethodSecurityConfiguration.class.isAssignableFrom(importingClass);
        AdviceMode mode = attributes.getEnum("mode");
	//前面沒有配置mode,所以autoProxyClassName為AutoProxyRegistrar.class.getName()
        String autoProxyClassName = AdviceMode.PROXY == mode ? AutoProxyRegistrar.class.getName()
                : GlobalMethodSecurityAspectJConfiguration.class.getName();
        if(skipMethodSecurityConfiguration) {
	//所以此方法最終返回只有"AutoProxyRegistrar"一個元素的字串陣列.
            return new String[] { autoProxyClassName };
        }
        return new String[] { autoProxyClassName,
                GlobalMethodSecurityConfiguration.class.getName()};
    }
}
緊跟著看AutoProxyRegistrar
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
	//前面省略
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean candidateFound = false;
		Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
		for (String annoType : annoTypes) {
			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
			Object mode = candidate.get("mode");
			Object proxyTargetClass = candidate.get("proxyTargetClass");
			if (mode != null && proxyTargetClass != null && mode.getClass().equals(AdviceMode.class) &&
					proxyTargetClass.getClass().equals(Boolean.class)) {
				candidateFound = true;
				if (mode == AdviceMode.PROXY) {
					//關鍵點在這裡
					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
					if ((Boolean) proxyTargetClass) {
						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
						return;
					}
				}
			}
		}
		//後面省略
	}
}
再看AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
	return registerAutoProxyCreatorIfNecessary(registry, null);
}
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
	return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
這裡就可以看出實際上,就註冊了一個InfrastructureAdvisorAutoProxyCreator這樣的BeanPostProcessor,建立代理就是要靠它了.只有自動代理建立器是無法建立代理的,必須要有Advice,或者Advisor.回頭看GlobalMethodSecurityConfiguration,這個配置就包含一個MethodSecurityMetadataSourceAdvisor,建立這個Advisor又使用了MethodSecurityInterceptor和MethodSecurityMetadataSource.


有了這些配置,就大概差不多了!代理的建立和前面介紹差不多,這裡就是InfrastructureAdvisorAutoProxyCreator這樣的Bean後置處理器根據MethodSecurityMetadataSourceAdvisor來建立代理了.


有了代理,再看看呼叫時機.前文介紹到JDK建立的代理,入口在org.springframework.aop.framework.JdkDynamicAopProxy#invoke;如果使用CGLib建立的代理,入口在org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept.最後都會呼叫org.springframework.aop.framework.ReflectiveMethodInvocation#proceed,具體到前面配置的spring security,最終得到MethodSecurityInterceptor,並呼叫invoke方法,這前面的工作的主要功勞都要歸於Spring AOP了.下面看看這個invoke方法:

public Object invoke(MethodInvocation mi) throws Throwable {
	//執行目標方法前呼叫,也就是驗證許可權就在這裡了.
	InterceptorStatusToken token = super.beforeInvocation(mi);
	Object result;
	try {
		result = mi.proceed();
	} finally {
		super.finallyInvocation(token);
	}
	return super.afterInvocation(token, result);
}
再看org.springframework.security.access.intercept.AbstractSecurityInterceptor#beforeInvocation這個方法
protected InterceptorStatusToken beforeInvocation(Object object) {
	//...略
	Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
	//...略
	Authentication authenticated = authenticateIfRequired();
	// Attempt authorization
	try {
		//關鍵點在這裡
		this.accessDecisionManager.decide(authenticated, object, attributes);
	}catch (AccessDeniedException accessDeniedException) {
		publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException));
		throw accessDeniedException;
	}
	//...略
}
其中authenticated就是使用者的認證資訊,object就是方法呼叫MethodInvocation例項,attributes就是從方法註解上獲取的配置資訊,根據這三個東東來作出決定使用者是否有許可權,繼續執行這個目標方法.剩下的也不必多解析了.