当前位置: 移动技术网 > IT编程>开发语言>Java > shiro中权限注解原理分析

shiro中权限注解原理分析

2019年08月20日  | 移动技术网IT编程  | 我要评论

史大平,翠鸟靶机,cz6427航班

概述

前不久刚学会使用权限注解(),开始思索了一番。最开始猜测实现方式是注解@aspect,具体实现方式类似如下所示(切面记录审计日志)。后来发现并非如此,所以特地分析一下源码。

@component
@aspect
public class auditlogaspectconfig {
    @pointcut("@annotation(com.ygsoft.ecp.mapp.basic.audit.annotation.auditlog) || @annotation(com.ygsoft.ecp.mapp.basic.audit.annotation.auditlogs)")
    public void pointcut() {        
    }

    @after(value="pointcut()")
    public void after(joinpoint joinpoint) {
        //执行的逻辑
    }
    ...
}

权限注解的源码分析

defaultadvisorautoproxycreator这个类实现了beanprocessor接口,当applicationcontext读取所有的bean配置信息后,这个类将扫描上下文,寻找所有的advistor(一个advisor是一个切入点和一个通知的组成),将这些advisor应用到所有符合切入点的bean中。

@configuration
public class shiroannotationprocessorconfiguration extends abstractshiroannotationprocessorconfiguration{
    @bean
    @dependson("lifecyclebeanpostprocessor")
    protected defaultadvisorautoproxycreator defaultadvisorautoproxycreator() {
        return super.defaultadvisorautoproxycreator();
    }

    @bean
    protected authorizationattributesourceadvisor authorizationattributesourceadvisor(securitymanager securitymanager) {
        return super.authorizationattributesourceadvisor(securitymanager);
    }

}

authorizationattributesourceadvisor继承了staticmethodmatcherpointcutadvisor,如下代码所示,只匹配五个注解,也就是说只对这五个注解标注的类或者方法增强。staticmethodmatcherpointcutadvisor是静态方法切点的抽象基类,默认情况下它匹配所有的类。staticmethodmatcherpointcut包括两个主要的子类分别是namematchmethodpointcutabstractregexpmethodpointcut,前者提供简单字符串匹配方法前面,而后者使用正则表达式匹配方法前面。动态方法切点:dynamicmethodmatcerpointcut是动态方法切点的抽象基类,默认情况下它匹配所有的类,而且也已经过时,建议使用defaultpointcutadvisordynamicmethodmatcherpointcut动态方法代替。另外还需关注构造器中的传入的aopallianceannotationsauthorizingmethodinterceptor

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;

    public authorizationattributesourceadvisor() {
        setadvice(new aopallianceannotationsauthorizingmethodinterceptor());
    }

    public securitymanager getsecuritymanager() {
        return 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;
        }
        
        if ( targetclass != null) {
            try {
                m = targetclass.getmethod(m.getname(), m.getparametertypes());
                if ( isauthzannotationpresent(m) ) {
                    return true;
                }
            } catch (nosuchmethodexception ignored) {
                
            }
        }

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

}

aopallianceannotationsauthorizingmethodinterceptor在初始化时,interceptors添加了5个方法拦截器(都继承自authorizingannotationmethodinterceptor),这5个拦截器分别对5种权限验证的方法进行拦截,执行invoke方法。

public class aopallianceannotationsauthorizingmethodinterceptor
        extends annotationsauthorizingmethodinterceptor implements methodinterceptor {

    public aopallianceannotationsauthorizingmethodinterceptor() {
        list<authorizingannotationmethodinterceptor> interceptors =
                new arraylist<authorizingannotationmethodinterceptor>(5);
        annotationresolver resolver = new springannotationresolver();
        
        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);
    }
    
    public object invoke(methodinvocation methodinvocation) throws throwable {
        org.apache.shiro.aop.methodinvocation mi = createmethodinvocation(methodinvocation);
        return super.invoke(mi);
    }
    ...
}

aopallianceannotationsauthorizingmethodinterceptor的invoke方法,又会调用超类authorizingmethodinterceptor的invoke方法,在该方法中先执行assertauthorized方法,进行权限校验,校验不通过,抛出authorizationexception异常,中断方法;校验通过,则执行methodinvocation.proceed(),该方法也就是被拦截并且需要权限校验的方法。

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

assertauthorized方法最终执行的还是authorizingannotationmethodinterceptor.assertauthorized,而authorizingannotationmethodinterceptor有5中的具体的实现类(roleannotationmethodinterceptor, permissionannotationmethodinterceptor, authenticatedannotationmethodinterceptor, userannotationmethodinterceptor, guestannotationmethodinterceptor)。

public abstract class annotationsauthorizingmethodinterceptor extends   authorizingmethodinterceptor {
  
    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);
                }
            }
        }
    }
    ...
}

authorizingannotationmethodinterceptor的assertauthorized,首先从子类获取authorizingannotationhandler,再调用该实现类的assertauthorized方法。

public abstract class authorizingannotationmethodinterceptor extends annotationmethodinterceptor
{

    public authorizingannotationmethodinterceptor( authorizingannotationhandler handler ) {
        super(handler);
    }

    public authorizingannotationmethodinterceptor( authorizingannotationhandler handler,
                                                   annotationresolver resolver) {
        super(handler, resolver);
    }

    public object invoke(methodinvocation methodinvocation) throws throwable {
        assertauthorized(methodinvocation);
        return methodinvocation.proceed();
    }

    public void assertauthorized(methodinvocation mi) throws authorizationexception {
        try {
            ((authorizingannotationhandler)gethandler()).assertauthorized(getannotation(mi));
        }
        catch(authorizationexception ae) {
            if (ae.getcause() == null) ae.initcause(new authorizationexception("not authorized to invoke method: " + mi.getmethod()));
            throw ae;
        }         
    }
}

现在分析其中一种实现类permissionannotationmethodinterceptor,也是用的最多的,但是这个类的实际代码很少,很明显上述分析的gethandler在permissionannotationmethodinterceptor中返回值为permissionannotationhandler

public class permissionannotationmethodinterceptor extends authorizingannotationmethodinterceptor {

    public permissionannotationmethodinterceptor() {
        super( new permissionannotationhandler() );
    }

 
    public permissionannotationmethodinterceptor(annotationresolver resolver) {
        super( new permissionannotationhandler(), resolver);
    }
}

permissionannotationhandler类中,终于发现实际的检验逻辑,还是调用的subject.checkpermission()进行校验。

public class permissionannotationhandler extends authorizingannotationhandler {

    public permissionannotationhandler() {
        super(requirespermissions.class);
    }

    protected string[] getannotationvalue(annotation a) {
        requirespermissions rpannotation = (requirespermissions) a;
        return rpannotation.value();
    }

    public void assertauthorized(annotation a) throws authorizationexception {
        if (!(a instanceof requirespermissions)) return;

        requirespermissions rpannotation = (requirespermissions) a;
        string[] perms = getannotationvalue(a);
        subject subject = getsubject();

        if (perms.length == 1) {
            subject.checkpermission(perms[0]);
            return;
        }
        if (logical.and.equals(rpannotation.logical())) {
            getsubject().checkpermissions(perms);
            return;
        }
        if (logical.or.equals(rpannotation.logical())) {
            boolean hasatleastonepermission = false;
            for (string permission : perms) if (getsubject().ispermitted(permission)) hasatleastonepermission = true;
            if (!hasatleastonepermission) getsubject().checkpermission(perms[0]);
            
        }
    }
}

实现类似编程式aop

定义一个注解

@target({elementtype.method})
@retention(retentionpolicy.runtime)
public @interface log {
    string value() default "";
}

继承staticmethodmatcherpointcutadvisor类,并实现相关的方法。

@suppresswarnings("serial")
@component
public class helloadvisor extends staticmethodmatcherpointcutadvisor{
    
    public helloadvisor() {
        setadvice(new logmethodinterceptor());
    }

    public boolean matches(method method, class targetclass) {
        method m = method;
        if ( isauthzannotationpresent(m) ) {
            return true;
        }

        if ( targetclass != null) {
            try {
                m = targetclass.getmethod(m.getname(), m.getparametertypes());
                return isauthzannotationpresent(m);
            } catch (nosuchmethodexception ignored) {
               
            }
        }
        return false;
    }

    private boolean isauthzannotationpresent(method method) {
        annotation a = annotationutils.findannotation(method, log.class);
        return a!= null;
    }
}

实现methodinterceptor接口,定义切面处理的逻辑

public class logmethodinterceptor implements methodinterceptor{

    public object invoke(methodinvocation invocation) throws throwable {
        log log = invocation.getmethod().getannotation(log.class);
        system.out.println("log: "+log.value());
        return invocation.proceed();    
    }
}

定义一个测试类,并添加log注解

@component
public class testhello {

    @log("test log")
    public string say() {
        return "ss";
    }
}

编写启动类,并且配置defaultadvisorautoproxycreator

@configuration
public class testboot {

    public static void main(string[] args) {
        applicationcontext ctx = new annotationconfigapplicationcontext("com.fzsyw.test");  
        testhello th = ctx.getbean(testhello.class);
        system.out.println(th.say());
    }
    
    @bean
    public defaultadvisorautoproxycreator defaultadvisorautoproxycreator(){
        defaultadvisorautoproxycreator da = new defaultadvisorautoproxycreator();
        da.setproxytargetclass(true);
        return da;
    }
}

最终打印的结果如下,证明编程式的aop生效。

log: test log
ss

总结与思考

shiro的注解式权限,使用确实方便,通过源码也分析了它的实现原理,比较核心的是配置defaultadvisorautoproxycreator和继承staticmethodmatcherpointcutadvisor。其中的5中权限注解,使用了了统一一套代码,用到了大量的模板模式,方便扩展。最后自己也简单做了一个小例子,加深对编程式aop的理解。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网