上一篇文章介绍了shiro在spring-boot中经过filter实现authentication流程(经过设置filterMaps也能够达到authorization的目的);这篇文章主要介绍spring经过AOP的方式实现shiro的authorization的流程。ios
shiro-spring-boot-web-starter
除了自身在META-INF中定义了ShiroWebAutoConfiguration
和ShiroWebFilterConfiguration
外,还在pom
文件中引用了shiro-spring-boot-stater
。然后者在本身的META-INF
文件中又定义了三个配置类:web
ShiroAutoConfiguration
:主要将shiro中重要的组件声明成bean。大部分配置被ShiroWebAutoConfiguration
中的bean取代。ShiroBeanAutoConfiguration
:主要设置了EventBus
(便于监听各类事件)和LifecycleBeanPostProcessor
(生命周期管理,对象的初始化和销毁)。ShiroAnnotationProcessorAutoConfiguration
:顾名思义,shiro注解处理相关的bean都在这个类中配置。@SuppressWarnings("SpringFacetCodeInspection") @Configuration @ConditionalOnProperty(name = "shiro.annotations.enabled", matchIfMissing = true) public class ShiroAnnotationProcessorAutoConfiguration extends AbstractShiroAnnotationProcessorConfiguration { //负责建立代理类的对象 @Bean @DependsOn("lifecycleBeanPostProcessor") @ConditionalOnMissingBean @Override public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { return super.defaultAdvisorAutoProxyCreator(); } //声明了Adviosr,Advisor声明了Pointcut和Advice,即规定了在哪些地方作哪些事 @Bean @ConditionalOnMissingBean @Override public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { return super.authorizationAttributeSourceAdvisor(securityManager); } }
因此shiro经过声明了Advisor,以AOP的方式在执行某些方法前先进行权限校验。spring
DefaultAdvisorAutoProxyCreator
是spring框架提供的用来建立代理的类。能够经过这个类理清spring建立代理的流程。先了解DefaultAdvisorAutoProxyCreator
的类继承关系。图中删除了部分继承关系,只保留了最主要的内容:
apache
从接口的继承关系中能够看到,该类的处理可能处于类的实例化先后(Instantiation)和初始化先后(Initialization)。
下面的分析将以Bean的建立流程为顺序。数组
postProcessBeforeInstantiation()
中。//实例化前置处理(该方法会在bean实例化前调用,且若是该方法返回不会空,则不会在建立bean的实例) @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { Object cacheKey = getCacheKey(beanClass, beanName); // if (beanName == null || !this.targetSourcedBeans.contains(beanName)) { //是否已经被代理过 if (this.advisedBeans.containsKey(cacheKey)) { return null; } //AOP相关的系统类 和 须要跳过的类(交由子类根据具体需求拓展) 不须要代理 if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } //若是定义了符合该Bean的TargetSource,那么使用TargetSource为该Bean建立代理 //TargetSource可让用户自定义代理的过程 if (beanName != null) { TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { this.targetSourcedBeans.add(beanName); Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } } return null; }
postProcessBeanBeforeInstantication
中已经建立了Bean的代理对象,则会跳过createBean
的过程。postProcessAfterInstantication()
:boolean
型的值,决定是否继续执行是剩下的InstantationAwareBeanPostProcessor
。postProcessBeforeInitialization()
:这里不对bean作任务处理直接返回。@Override public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; }
postProcessAfterInitialization()
。这一步是spring建立代理的过程。@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { //获取缓存的key Object cacheKey = getCacheKey(bean.getClass(), beanName); //获取是否在以前已经对其代理过 if (!this.earlyProxyReferences.contains(cacheKey)) { //若是须要代理,则对其进行包装 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
其中的wrapIfNecessary
就是为bean建立代理的过程。先判断该bean是否须要建立代理,若是须要则建立代理封装该bean。缓存
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //判断是否已经由TargetSource产生过代理 if (beanName != null && this.targetSourcedBeans.contains(beanName)) { return bean; } //判断是否已经解析过该bean,且结果是不须要代理 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } //判断是不是AOP相关类 或是 不须要代理的类 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } //获取该Bean相关的Advice Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); //若是不等于空,则说明须要代理 if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); //建立代理 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
决定这个bean是否要代理的一个重要过程是getAdvicesAndAdvisorsForBean()
。这个方法会返回须要应用在该bean上的advice或是advisor。若是返回为空,则说明不须要代理。这个方法的具体实现是在AbstractAdvisorAutoProxyCreator
。app
//获取能够应用在该bean上的advise或advisor @Override protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) { //具体查找方法交给findEligibleAdvisors实现 List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); //若是没找到,则返回特定对象 表示不须要代理 if (advisors.isEmpty()) { return DO_NOT_PROXY; } //不然转成数组返回 return advisors.toArray(); } //查询核实的advisor方法 protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { //找出全部的advisor作候选 List<Advisor> candidateAdvisors = findCandidateAdvisors(); //再在候选的advisor筛选出适用的 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); //拓展Advisor extendAdvisors(eligibleAdvisors); //排序 if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; } //查找候选advisor的方法委托给BeanFactoryAdvisorRetrievalHelper protected List<Advisor> findCandidateAdvisors() { return this.advisorRetrievalHelper.findAdvisorBeans(); } //获取适用的Advisor,主要委托给AopUtil protected List<Advisor> findAdvisorsThatCanApply( List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { ProxyCreationContext.setCurrentProxiedBeanName(null); } } /** * Return whether the Advisor bean with the given name is eligible * for proxying in the first place. * @param beanName the name of the Advisor bean * @return whether the bean is eligible */ protected boolean isEligibleAdvisorBean(String beanName) { return true; } //对Advisor排序 protected List<Advisor> sortAdvisors(List<Advisor> advisors) { AnnotationAwareOrderComparator.sort(advisors); return advisors; }
BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()
大概过程就是先经过在beanFactory中查询类型为Advisor.class
或其子类的的bean的name。而后根据beanName,再从beanFactory中根据beanName获取对应的Advisor
的bean。框架
public List<Advisor> findAdvisorBeans() { // 若是已经缓存过,则直接使用缓存的结果 String[] advisorNames = this.cachedAdvisorBeanNames; //没缓存 则在BeanFactory中搜索一次 if (advisorNames == null) { // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the auto-proxy creator apply to them! //根据Advisor类型查询 //这里只是获取bean的name,并未进行实例化 advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } if (advisorNames.length == 0) { return new ArrayList<Advisor>(); } List<Advisor> advisors = new ArrayList<Advisor>(); //根据beanName获取对应的Advisor的bean for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isDebugEnabled()) { logger.debug("Skipping currently created advisor '" + name + "'"); } } else { try { //实例化advisor的bean advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException) { BeanCreationException bce = (BeanCreationException) rootCause; if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) { if (logger.isDebugEnabled()) { logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } // Ignore: indicates a reference back to the bean we're trying to advise. // We want to find advisors other than the currently created bean itself. continue; } } throw ex; } } } } return advisors; }
再来看决定Advisors是否适用的过程:AopUtils.findAdvisorsThatCanApply()
。ide
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List<Advisor> eligibleAdvisors = new LinkedList<Advisor>(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor) { // already processed continue; } if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
主要是将Advisor根据不一样的类型分红两类:IntroducationAdvisor
和PointcutAdvisor
。两种Advisor由于类型不一样,因此判断方式也不同。IntroductionAdvisor
由于是类级别的拦截,它描述的”切点“是针对类,因此是经过ClassFilter
来判断。而PointcutAdvisor
能够针对方法,经过Pointcut
描述切点。这点能够从canApply()
中看出。spring-boot
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { if (advisor instanceof IntroductionAdvisor) { //IntroductionAdvisor直接经过classFilter匹配 return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } else if (advisor instanceof PointcutAdvisor) { //PointcutAdvisor则是经过pointcut,在调用canApply的重载方法实现 PointcutAdvisor pca = (PointcutAdvisor) advisor; return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn't have a pointcut so we assume it applies. return true; } }
找到Advisor以后,剩下的就是建立代理的过程。回到wrapIfNecessary
,建立代理的过程在createProxy()
中。
//建立代理对象 protected Object createProxy( Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } //建立代理工厂类,而且拷贝须要的配置 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } //将拦截器封装成advisor Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); //设置拦截器和TargetSource proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); //留给子类根据须要拓展 customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } //建立代理对象 return proxyFactory.getProxy(getProxyClassLoader()); }
上述方法中主要是建立了ProxyFactory
对象,并设置属性,在经过ProxyFactory
对象建立代理对象。
最后返回的代理对象便取代了原始的bean对象保存在spring容器中待取用。
若是对上述流程图还有不清楚的地方,能够参考我画的流程图。
经过上述流程咱们了解了spring如何根据advisor建立代理。如今咱们要了解的是shiro的advisor:AuthorizationAttributeSourceAdvisor
。
类的关系图:
从图中咱们能够了解到AuthorizationAttributeSourceAdvisor
是一个PointcutAdvisor
。若是看代码的话你会发现Pointcut
设置的ClassFilter
是TureClassFilter
,也就是说它对任何类判断都会是经过,只校验方法是否正确。所以AuthorizationAttributeSourceAdvisor
中最重要的方法就是matches
:
@SuppressWarnings({"unchecked"}) public class AuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor { private static final Logger log = LoggerFactory.getLogger(AuthorizationAttributeSourceAdvisor.class); private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES = new Class[] { RequiresPermissions.class, RequiresRoles.class, RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class }; protected SecurityManager securityManager = null; /** * Create a new AuthorizationAttributeSourceAdvisor. */ public AuthorizationAttributeSourceAdvisor() { //设置通知 setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor()); } public SecurityManager getSecurityManager() { return securityManager; } //设置SecurityManager public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) { this.securityManager = securityManager; } public boolean matches(Method method, Class targetClass) { Method m = method; if ( isAuthzAnnotationPresent(m) ) { return true; } //The 'method' parameter could be from an interface that doesn't have the annotation. //Check to see if the implementation has it. if ( targetClass != null) { try { m = targetClass.getMethod(m.getName(), m.getParameterTypes()); //判断方法或是类上是否有shiro关注的注解 return isAuthzAnnotationPresent(m) || isAuthzAnnotationPresent(targetClass); } catch (NoSuchMethodException ignored) { //default return value is false. If we can't find the method, then obviously //there is no annotation, so just use the default return value. } } return false; } private boolean isAuthzAnnotationPresent(Class<?> targetClazz) { for( Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES ) { Annotation a = AnnotationUtils.findAnnotation(targetClazz, annClass); if ( a != null ) { return true; } } return false; } private boolean isAuthzAnnotationPresent(Method method) { for( Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES ) { Annotation a = AnnotationUtils.findAnnotation(method, annClass); if ( a != null ) { return true; } } return false; } }
除了Advisor的matches
方法外,还须要关注到的是Advisor设置的advise对象:AopAllianceAnnotationsAuthorizingMethodInterceptor
。
我的的理解是AopAllianceAnnotattionsAuthorizingMethodInterceptor
是将shiro框架中的MethodInterceptor
和aopalliance框架中的MethodInterceptor
作了适配,让shiro的处理过程转变成aopalliance的MethodIntercetor
的处理过程。然后者是咱们所熟悉的spring的拦截器。
上图能够看到同时实现了两个MethodInterceptor
接口。
AopAllianceAnnotationsAuthorizingMethodInterceptor
代码相对简单。
public class AopAllianceAnnotationsAuthorizingMethodInterceptor extends AnnotationsAuthorizingMethodInterceptor implements MethodInterceptor { public AopAllianceAnnotationsAuthorizingMethodInterceptor() { List<AuthorizingAnnotationMethodInterceptor> interceptors = new ArrayList<AuthorizingAnnotationMethodInterceptor>(5); //配置shiro拦截器 AnnotationResolver resolver = new SpringAnnotationResolver(); //we can re-use the same resolver instance - it does not retain state: interceptors.add(new RoleAnnotationMethodInterceptor(resolver)); interceptors.add(new PermissionAnnotationMethodInterceptor(resolver)); interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver)); interceptors.add(new UserAnnotationMethodInterceptor(resolver)); interceptors.add(new GuestAnnotationMethodInterceptor(resolver)); setMethodInterceptors(interceptors); } /** * Creates a {@link MethodInvocation MethodInvocation} that wraps an * {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} instance, * enabling Shiro Annotations in <a href="http://aopalliance.sourceforge.net/">AOP Alliance</a> environments * (Spring, etc). * * @param implSpecificMethodInvocation AOP Alliance {@link org.aopalliance.intercept.MethodInvocation MethodInvocation} * @return a Shiro {@link MethodInvocation MethodInvocation} instance that wraps the AOP Alliance instance. */ protected org.apache.shiro.aop.MethodInvocation createMethodInvocation(Object implSpecificMethodInvocation) { final MethodInvocation mi = (MethodInvocation) implSpecificMethodInvocation; return new org.apache.shiro.aop.MethodInvocation() { public Method getMethod() { return mi.getMethod(); } public Object[] getArguments() { return mi.getArguments(); } public String toString() { return "Method invocation [" + mi.getMethod() + "]"; } public Object proceed() throws Throwable { return mi.proceed(); } public Object getThis() { return mi.getThis(); } }; } /** * Simply casts the method argument to an * {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} and then * calls <code>methodInvocation.{@link org.aopalliance.intercept.MethodInvocation#proceed proceed}()</code> * * @param aopAllianceMethodInvocation the {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} * @return the {@link org.aopalliance.intercept.MethodInvocation#proceed() org.aopalliance.intercept.MethodInvocation.proceed()} method call result. * @throws Throwable if the underlying AOP Alliance <code>proceed()</code> call throws a <code>Throwable</code>. */ protected Object continueInvocation(Object aopAllianceMethodInvocation) throws Throwable { MethodInvocation mi = (MethodInvocation) aopAllianceMethodInvocation; return mi.proceed(); } //经过spring中的拦截器机制发起拦截,并将处理转换成shiro的拦截器处理过程,是一个适配的过程 public Object invoke(MethodInvocation methodInvocation) throws Throwable { //将spring的MethodInvocation转换成shiro的MethodInvocation对象 org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation); //调用AuthorizingMethodInterceptor的invoke方法 return super.invoke(mi); } }
AuthorizingMethodInterceptor
的invoke则会调用asserAuthorized
方法。
public abstract class AuthorizingMethodInterceptor extends MethodInterceptorSupport { //拦截器方法被调用 public Object invoke(MethodInvocation methodInvocation) throws Throwable { assertAuthorized(methodInvocation); return methodInvocation.proceed(); } //受权判断,交给子类实现 protected abstract void assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException; }
AnnotationAuthorizingMethodInterceptor
方法实现了assertAuthorized
方法,遍历其配置的AuthorizingAnnotationMethodInterceptor
对象,若是匹配则进行验证。
public abstract class AnnotationsAuthorizingMethodInterceptor extends AuthorizingMethodInterceptor { /** * The method interceptors to execute for the annotated method. */ protected Collection<AuthorizingAnnotationMethodInterceptor> methodInterceptors; public AnnotationsAuthorizingMethodInterceptor() { //配置默认的权限认证拦截器 methodInterceptors = new ArrayList<AuthorizingAnnotationMethodInterceptor>(5); methodInterceptors.add(new RoleAnnotationMethodInterceptor()); methodInterceptors.add(new PermissionAnnotationMethodInterceptor()); methodInterceptors.add(new AuthenticatedAnnotationMethodInterceptor()); methodInterceptors.add(new UserAnnotationMethodInterceptor()); methodInterceptors.add(new GuestAnnotationMethodInterceptor()); } public Collection<AuthorizingAnnotationMethodInterceptor> getMethodInterceptors() { return methodInterceptors; } public void setMethodInterceptors(Collection<AuthorizingAnnotationMethodInterceptor> methodInterceptors) { this.methodInterceptors = methodInterceptors; } //遍历全部权限认证拦截器,若是拦截器支持,则使用拦截器认证 protected void assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException { //default implementation just ensures no deny votes are cast: Collection<AuthorizingAnnotationMethodInterceptor> aamis = getMethodInterceptors(); if (aamis != null && !aamis.isEmpty()) { for (AuthorizingAnnotationMethodInterceptor aami : aamis) { if (aami.supports(methodInvocation)) { aami.assertAuthorized(methodInvocation); } } } } }
而权限认证拦截器则是将具体认证过程委托给内部的Handler
对象处理。所以拦截器处理的过程大体以下:
AopAllianceAnnotationAuthorizingMethodInterceptor
的invoke
方法被调用assertAuthorized()
assertAuthorized
方法AuthorizingAnnotationHandler
处理RoleAnnotationHandler
为例,它会在本身的assertAuthorized
方法中校验Subject
对象的Role
和@RequiredRole
中要求的是否一致,不一致则会抛出异常,拦截器不在往下走,由于也没法进入到被拦截的方法里。Shiro权限认证的过程是经过AOP动态代理实现的。至关于在Spring中配置了一个用于权限认证的拦截器,拦截拥有指定注解(@RequiresAuthentication
,@RequiresUser
,@RequiresGuest
,@RequiresRoles
,@RequiresPermissions
)的方法。