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


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

ShiroAnnotationProcessorAutoConfiguration

shiro-spring-boot-web-starter除了自身在META-INF中定義了ShiroWebAutoConfigurationShiroWebFilterConfiguration外,還在pom文件中引用了shiro-spring-boot-stater。而后者在自己的META-INF文件中又定義了三個配置類:

  • 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的方式在執行某些方法前先進行權限校驗。

DefaultAdvisorAutoProxyCreator和創建代理的流程

DefaultAdvisorAutoProxyCreator是spring框架提供的用來創建代理的類。可以通過這個類理清spring創建代理的流程。先了解DefaultAdvisorAutoProxyCreator的類繼承關系。圖中刪除了部分繼承關系,只保留了最主要的內容:

從接口的繼承關系中可以看到,該類的處理可能處於類的實例化前后(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。如果返回為空,則說明不需要代理。這個方法的具體實現是在AbstractAdvisorAutoProxyCreator

//獲取可以應用在該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()

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()中看出。

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)的方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM