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的类继承关系。图中删除了部分继承关系,只保留了最主要的内容:
887326-20190618193820086-1742076792.png

从接口的继承关系中可以看到,该类的处理可能处于类的实例化前后(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容器中待取用。
如果对上述流程图还有不清楚的地方,可以参考我画的流程图。
887326-20190618193937278-1084649301.png

shiro生命的Advisor:AuthorizationAttributeSourceAdvisor

通过上述流程我们了解了spring如何根据advisor创建代理。现在我们要了解的是shiro的advisor:AuthorizationAttributeSourceAdvisor
类的关系图:
887326-20190618193951198-1755984631.png

从图中我们可以了解到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的拦截器。
887326-20190618194020716-2083211044.png

上图可以看到同时实现了两个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)的方法。

转载于:https://www.cnblogs.com/insaneXs/p/11047194.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值