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就是从方法注解上获取的配置信息,根据这三个东东来作出决定用户是否有权限,继续执行这个目标方法.剩下的也不必多解析了.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值