Shiro(三):Spring-boot如何集成Shiro(下)

上一篇文章介紹了shiro在spring-boot中經過filter實現authentication流程(經過設置filterMaps也能夠達到authorization的目的);這篇文章主要介紹spring經過AOP的方式實現shiro的authorization的流程。ios

ShiroAnnotationProcessorAutoConfiguration

shiro-spring-boot-web-starter除了自身在META-INF中定義了ShiroWebAutoConfigurationShiroWebFilterConfiguration外,還在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和建立代理的流程

DefaultAdvisorAutoProxyCreator是spring框架提供的用來建立代理的類。能夠經過這個類理清spring建立代理的流程。先了解DefaultAdvisorAutoProxyCreator的類繼承關係。圖中刪除了部分繼承關係,只保留了最主要的內容:
apache

從接口的繼承關係中能夠看到,該類的處理可能處於類的實例化先後(Instantiation)和初始化先後(Initialization)。
下面的分析將以Bean的建立流程爲順序。數組

  1. 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;
    }
  1. 建立Bean實例:
    若是在postProcessBeanBeforeInstantication中已經建立了Bean的代理對象,則會跳過createBean的過程。
  2. 實例化後置處理postProcessAfterInstantication()
    該方法返回boolean型的值,決定是否繼續執行是剩下的InstantationAwareBeanPostProcessor
  3. 初始化前置處理postProcessBeforeInitialization():這裏不對bean作任務處理直接返回。
@Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }
  1. bean初始化,這個階段可能會設置bean的屬性
  2. 初始化後置處理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。若是返回爲空,則說明不須要代理。這個方法的具體實現是在AbstractAdvisorAutoProxyCreatorapp

//獲取能夠應用在該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根據不一樣的類型分紅兩類:IntroducationAdvisorPointcutAdvisor。兩種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容器中待取用。
若是對上述流程圖還有不清楚的地方,能夠參考我畫的流程圖。

shiro生命的Advisor:AuthorizationAttributeSourceAdvisor

經過上述流程咱們瞭解了spring如何根據advisor建立代理。如今咱們要了解的是shiro的advisor:AuthorizationAttributeSourceAdvisor
類的關係圖:

從圖中咱們能夠了解到AuthorizationAttributeSourceAdvisor是一個PointcutAdvisor。若是看代碼的話你會發現Pointcut設置的ClassFilterTureClassFilter,也就是說它對任何類判斷都會是經過,只校驗方法是否正確。所以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對象處理。所以攔截器處理的過程大體以下:

  1. AopAllianceAnnotationAuthorizingMethodInterceptorinvoke方法被調用
  2. 調用assertAuthorized()
  3. 獲取內部配置的認證攔截器,逐個調用assertAuthorized方法
  4. 內部認證攔截器將認證委託給內部的AuthorizingAnnotationHandler處理
  5. RoleAnnotationHandler爲例,它會在本身的assertAuthorized方法中校驗Subject對象的Role@RequiredRole中要求的是否一致,不一致則會拋出異常,攔截器不在往下走,由於也沒法進入到被攔截的方法裏。

總結

Shiro權限認證的過程是經過AOP動態代理實現的。至關於在Spring中配置了一個用於權限認證的攔截器,攔截擁有指定註解(@RequiresAuthentication@RequiresUser@RequiresGuest@RequiresRoles@RequiresPermissions)的方法。

相關文章
相關標籤/搜索