更新時(shí)間:2022-02-08 10:43:12 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽937次
分析一下源碼。
@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) {
//執(zhí)行的邏輯
}
...
}
DefaultAdvisorAutoProxyCreator這個(gè)類實(shí)現(xiàn)了BeanProcessor接口,當(dāng)ApplicationContext讀取所有的Bean配置信息后,這個(gè)類將掃描上下文,尋找所有的Advistor(一個(gè)Advisor是一個(gè)切入點(diǎn)和一個(gè)通知的組成),將這些Advisor應(yīng)用到所有符合切入點(diǎn)的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,如下代碼所示,只匹配五個(gè)注解,也就是說(shuō)只對(duì)這五個(gè)注解標(biāo)注的類或者方法增強(qiáng)。StaticMethodMatcherPointcutAdvisor是靜態(tài)方法切點(diǎn)的抽象基類,默認(rèn)情況下它匹配所有的類。StaticMethodMatcherPointcut包括兩個(gè)主要的子類分別是NameMatchMethodPointcut和AbstractRegexpMethodPointcut,前者提供簡(jiǎn)單字符串匹配方法前面,而后者使用正則表達(dá)式匹配方法前面。動(dòng)態(tài)方法切點(diǎn):DynamicMethodMatcerPointcut是動(dòng)態(tài)方法切點(diǎn)的抽象基類,默認(rèn)情況下它匹配所有的類,而且也已經(jīng)過(guò)時(shí),建議使用DefaultPointcutAdvisor和DynamicMethodMatcherPointcut動(dòng)態(tài)方法代替。另外還需關(guān)注構(gòu)造器中的傳入的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在初始化時(shí),interceptors添加了5個(gè)方法攔截器(都繼承自AuthorizingAnnotationMethodInterceptor),這5個(gè)攔截器分別對(duì)5種權(quán)限驗(yàn)證的方法進(jìn)行攔截,執(zhí)行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方法,又會(huì)調(diào)用超類AuthorizingMethodInterceptor的invoke方法,在該方法中先執(zhí)行assertAuthorized方法,進(jìn)行權(quán)限校驗(yàn),校驗(yàn)不通過(guò),拋出AuthorizationException異常,中斷方法;校驗(yàn)通過(guò),則執(zhí)行methodInvocation.proceed(),該方法也就是被攔截并且需要權(quán)限校驗(yàn)的方法。
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方法最終執(zhí)行的還是AuthorizingAnnotationMethodInterceptor.assertAuthorized,而AuthorizingAnnotationMethodInterceptor有5中的具體的實(shí)現(xiàn)類(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,再調(diào)用該實(shí)現(xiàn)類的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;
}
}
}
現(xiàn)在分析其中一種實(shí)現(xiàn)類PermissionAnnotationMethodInterceptor,也是用的最多的,但是這個(gè)類的實(shí)際代碼很少,很明顯上述分析的getHandler在PermissionAnnotationMethodInterceptor中返回值為PermissionAnnotationHandler。
public class PermissionAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
public PermissionAnnotationMethodInterceptor() {
super( new PermissionAnnotationHandler() );
}
public PermissionAnnotationMethodInterceptor(AnnotationResolver resolver) {
super( new PermissionAnnotationHandler(), resolver);
}
}
在PermissionAnnotationHandler類中,終于發(fā)現(xiàn)實(shí)際的檢驗(yàn)邏輯,還是調(diào)用的Subject.checkPermission()進(jìn)行校驗(yàn)。
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]);
}
}
}
定義一個(gè)注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}
繼承StaticMethodMatcherPointcutAdvisor類,并實(shí)現(xiàn)相關(guān)的方法。
@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;
}
}
實(shí)現(xiàn)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();
}
}
定義一個(gè)測(cè)試類,并添加Log注解
@Component
public class TestHello {
@Log("test log")
public String say() {
return "ss";
}
}
編寫啟動(dòng)類,并且配置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;
}
}
最終打印的結(jié)果如下,證明編程式的AOP生效。
log: test log
ss
Shiro的注解式權(quán)限,使用確實(shí)方便,通過(guò)源碼也分析了它的實(shí)現(xiàn)原理,比較核心的是配置DefaultAdvisorAutoProxyCreator和繼承StaticMethodMatcherPointcutAdvisor。其中的5中權(quán)限注解,使用了統(tǒng)一一套代碼架構(gòu),用到了的模板模式,方便擴(kuò)展。
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
有基礎(chǔ) 直達(dá)就業(yè)
業(yè)余時(shí)間 高薪轉(zhuǎn)行
工作1~3年,加薪神器
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問(wèn)老師會(huì)電話與您溝通安排學(xué)習(xí)
初級(jí) 202925
初級(jí) 203221
初級(jí) 202629
初級(jí) 203743