使用以下配置就可启用spring security全局方法:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
有了这些配置,就大概差不多了!代理的创建和前面介绍差不多,这里就是InfrastructureAdvisorAutoProxyCreator这样的Bean后置处理器根据MethodSecurityMetadataSourceAdvisor来创建代理了.
@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就是从方法注解上获取的配置信息,根据这三个东东来作出决定用户是否有权限,继续执行这个目标方法.剩下的也不必多解析了.