栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Shiro之@RequiresPermissions注解原理详解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Shiro之@RequiresPermissions注解原理详解

前言

shiro为我们提供了几个权限注解,如下图:

这几个注解原理都类似,这里我们讲解@RequiresPermissions的原理。

铺垫

第一
首先要清楚@RequiresPermissions的基本用法。就是在Controller的方法里,加上@RequiresPermissions注解,并写上权限标识。那么,有该权限标识的用户,才能访问到请求。如下图:

第二
先剧透一下,@RequiresPermissions注解的原理是使用了Spring的AOP进行了增强。来判断当前用户是否有该权限标识。我们复习一下Spring的AOP原理。AOP的核心流程是ReflectiveMethodInvocation类中的proceed方法。该方法会遍历加强集合,执行每一个加强的方法。不熟悉的可以查看这篇博客:《Spring之Joinpoint类详解》

执行流程分析

有了上面的铺垫,我们开始从源码分析其执行流程。
首先,断点打到ReflectiveMethodInvocation类的proceed方法这里,因为要对代理对象进行增强,必定要走这里。然后发送请求,进入断点,我们先看加强链上有哪些元素:

第一个Expose开头的对象,是Spring自己生成的元素,我们无需关注。第二个元素,就是加强@RequiresPermissions而生成的元素。看该类的源码:

package org.apache.shiro.spring.security.interceptor;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.shiro.aop.AnnotationResolver;
import org.apache.shiro.authz.aop.*;
import org.apache.shiro.spring.aop.SpringAnnotationResolver;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;


public class AopAllianceAnnotationsAuthorizingMethodInterceptor
        extends AnnotationsAuthorizingMethodInterceptor implements MethodInterceptor {

    public AopAllianceAnnotationsAuthorizingMethodInterceptor() {
        List interceptors =
                new ArrayList(5);

        //use a Spring-specific Annotation resolver - Spring's AnnotationUtils is nicer than the
        //raw JDK resolution process.
        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);
    }
    
    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();
            }
        };
    }

    
    protected Object continueInvocation(Object aopAllianceMethodInvocation) throws Throwable {
        MethodInvocation mi = (MethodInvocation) aopAllianceMethodInvocation;
        return mi.proceed();
    }

    
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
        return super.invoke(mi);
    }
}

可以看出,这个类实现了MethodInterceptor接口。那么,其invoke方法,就是对原对象方法的增强。点进invoke方法,最后调到了AnnotationsAuthorizingMethodInterceptor类的assertAuthorized方法,如下:

protected void assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException {
        //default implementation just ensures no deny votes are cast:
        Collection aamis = getMethodInterceptors();
        if (aamis != null && !aamis.isEmpty()) {
            for (AuthorizingAnnotationMethodInterceptor aami : aamis) {
                if (aami.supports(methodInvocation)) {
                    aami.assertAuthorized(methodInvocation);
                }
            }
        }
    }

在这里,会遍历是否有前言中截图的那几个注解,如果有的话,则调用相应的注解Handler去处理相应的逻辑。@RequiresPermissions的Handler的处理逻辑就是调用Realm中定义的权限获取方法,判断是否有注解中的标识,具体的调用Realm流程不再解析,前面博客已经解析过。
这样,就对@RequiresPermissions注解的方法进行了增强。

@RequiresPermissions切面

使用AOP,一定要有切面,这样Spring在实例化bean过程中,才会生成代理对象和加强链。Shiro的切面是AuthorizationAttributeSourceAdvisor类,需要加入Spring容器中管理,配置如下:

 
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("securityManager") SecurityManager securityManager)
    {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

下面分析这个类源码。前面博客说过,Advisor切面的两大核心就是切点pointcut和增强advice。我们看AuthorizationAttributeSourceAdvisor 的这两个核心在哪儿:

 
    public AuthorizationAttributeSourceAdvisor() {
        setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor());
    }

这里设置了增强advice。正是我们上面分析的AopAllianceAnnotationsAuthorizingMethodInterceptor类。

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());
                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;
    }

matches方法定义了切点。就是在对@RequiresPermissions等注解收集,然后判断是不是切点。这样,切面就分析完了。

总结

shiro定义了AuthorizationAttributeSourceAdvisor 切面,在切面里,定义了AopAllianceAnnotationsAuthorizingMethodInterceptor增强,来查询Realm中用户的权限等信息,判断是否和参数中的数据匹配。定义了matches方法,来收集@RequiresPermissions等注解。
在Spring实例化Bean时,执行BeanPostProcessor的postProcessAfterInitialization方法生成代理对象时(循环依赖的情况除外),就会找到shiro定义的切面,生成相应的代理对象,当执行方法时,就会走增强的方法,进行方法增强。
所以对于shiro框架的使用者而言,只需将AuthorizationAttributeSourceAdvisor 切面加入到Spring容器,然后在Realm中定义用户的权限查询方法。然后在需要的方法上加@RequiresPermissions注解,就可以进行权限的控制了。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/748945.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号