shiro中权限注解原理分析
概述
前不久刚学会使用权限注解(),开始思索了一番。最开始猜测实现方式是注解@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
包括两个主要的子类分别是namematchmethodpointcut
和abstractregexpmethodpointcut
,前者提供简单字符串匹配方法前面,而后者使用正则表达式匹配方法前面。动态方法切点:dynamicmethodmatcerpointcut
是动态方法切点的抽象基类,默认情况下它匹配所有的类,而且也已经过时,建议使用defaultpointcutadvisor
和dynamicmethodmatcherpointcut
动态方法代替。另外还需关注构造器中的传入的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的理解。