AuthorizingAnnotationMethodInterceptor
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.shiro.authz.aop; import org.apache.shiro.aop.AnnotationMethodInterceptor; import org.apache.shiro.aop.AnnotationResolver; import org.apache.shiro.aop.MethodInvocation; import org.apache.shiro.authz.AuthorizationException; /** * An <tt>AnnotationMethodInterceptor</tt> that asserts the calling code is authorized to execute the method * before allowing the invocation to continue by inspecting code annotations to perform an access control check. * * @since 0.1 */ public abstract class AuthorizingAnnotationMethodInterceptor extends AnnotationMethodInterceptor { /** * Constructor that ensures the internal <code>handler</code> is set which will be used to perform the * authorization assertion checks when a supported annotation is encountered. * @param handler the internal <code>handler</code> used to perform authorization assertion checks when a * supported annotation is encountered. * * 注入AuthorizingAnnotationHandler权限注解处理器提供给父类AnnotationMethodInterceptor */ public AuthorizingAnnotationMethodInterceptor( AuthorizingAnnotationHandler handler ) { super(handler); } /** * * @param handler * @param resolver * @since 1.1 * * 注入AuthorizingAnnotationHandler权限注解处理器和AnnotationResolver注解解析器提供给父类AnnotationMethodInterceptor */ public AuthorizingAnnotationMethodInterceptor( AuthorizingAnnotationHandler handler, AnnotationResolver resolver) { super(handler, resolver); } /** * Ensures the <code>methodInvocation</code> is allowed to execute first before proceeding by calling the * {@link #assertAuthorized(org.apache.shiro.aop.MethodInvocation) assertAuthorized} method first. * * @param methodInvocation the method invocation to check for authorization prior to allowing it to proceed/execute. * @return the return value from the method invocation (the value of {@link org.apache.shiro.aop.MethodInvocation#proceed() MethodInvocation.proceed()}). * @throws org.apache.shiro.authz.AuthorizationException if the <code>MethodInvocation</code> is not allowed to proceed. * @throws Throwable if any other error occurs. * * 调用核心方法assertAuthorized,执行目标方法 */ public Object invoke(MethodInvocation methodInvocation) throws Throwable { assertAuthorized(methodInvocation); return methodInvocation.proceed(); } /** * Ensures the calling Subject is authorized to execute the specified <code>MethodInvocation</code>. * <p/> * As this is an AnnotationMethodInterceptor, this implementation merely delegates to the internal * {@link AuthorizingAnnotationHandler AuthorizingAnnotationHandler} by first acquiring the annotation by * calling {@link #getAnnotation(MethodInvocation) getAnnotation(methodInvocation)} and then calls * {@link AuthorizingAnnotationHandler#assertAuthorized(java.lang.annotation.Annotation) handler.assertAuthorized(annotation)}. * * @param mi the <code>MethodInvocation</code> to check to see if it is allowed to proceed/execute. * @throws AuthorizationException if the method invocation is not allowed to continue/execute. * * 获得AuthorizingAnnotationHandler权限注解处理器来处理判断是否有权限 */ public void assertAuthorized(MethodInvocation mi) throws AuthorizationException { try {
// 由父类那获得自己曾经提供的权限注解处理器,执行处理工作,后文详解#1 ((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi)); } catch(AuthorizationException ae) { // Annotation handler doesn't know why it was called, so add the information here if possible. // Don't wrap the exception here since we don't want to mask the specific exception, such as // UnauthenticatedException etc. if (ae.getCause() == null) ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod())); throw ae; } } }
AnnotationMethodInterceptor
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.shiro.aop; import java.lang.annotation.Annotation; /** * MethodInterceptor that inspects a specific annotation on the method invocation before continuing * its execution. * </p> * The annotation is acquired from the {@link MethodInvocation MethodInvocation} via a * {@link AnnotationResolver AnnotationResolver} instance that may be configured. Unless * overridden, the default {@code AnnotationResolver} is a * * @since 0.9 */ public abstract class AnnotationMethodInterceptor extends MethodInterceptorSupport { // 核心注解处理类,根据目标方法提供的注解信息进行权限判断 private AnnotationHandler handler; /** * The resolver to use to find annotations on intercepted methods. * * @since 1.1 * * 核心注解解析器,根据org.apache.shiro.aop.MethodInvocation提供的Method或Class信息和注解Class信息获得注解 */ private AnnotationResolver resolver; /** * Constructs an <code>AnnotationMethodInterceptor</code> with the * {@link AnnotationHandler AnnotationHandler} that will be used to process annotations of a * corresponding type. * * @param handler the handler to delegate to for processing the annotation. * * 支持注入注解处理器,创建默认的注解解析器 */ public AnnotationMethodInterceptor(AnnotationHandler handler) { this(handler, new DefaultAnnotationResolver()); } /** * Constructs an <code>AnnotationMethodInterceptor</code> with the * {@link AnnotationHandler AnnotationHandler} that will be used to process annotations of a * corresponding type, using the specified {@code AnnotationResolver} to acquire annotations * at runtime. * * @param handler the handler to use to process any discovered annotation * @param resolver the resolver to use to locate/acquire the annotation * @since 1.1 * * 支持注入注解处理器和注解解析器 */ public AnnotationMethodInterceptor(AnnotationHandler handler, AnnotationResolver resolver) { if (handler == null) { throw new IllegalArgumentException("AnnotationHandler argument cannot be null."); } setHandler(handler); setResolver(resolver != null ? resolver : new DefaultAnnotationResolver()); } /** * Returns the {@code AnnotationHandler} used to perform authorization behavior based on * an annotation discovered at runtime. * * @return the {@code AnnotationHandler} used to perform authorization behavior based on * an annotation discovered at runtime. */ public AnnotationHandler getHandler() { return handler; } /** * Sets the {@code AnnotationHandler} used to perform authorization behavior based on * an annotation discovered at runtime. * * @param handler the {@code AnnotationHandler} used to perform authorization behavior based on * an annotation discovered at runtime. */ public void setHandler(AnnotationHandler handler) { this.handler = handler; } /** * Returns the {@code AnnotationResolver} to use to acquire annotations from intercepted * methods at runtime. The annotation is then used by the {@link #getHandler handler} to * perform authorization logic. * * @return the {@code AnnotationResolver} to use to acquire annotations from intercepted * methods at runtime. * @since 1.1 */ public AnnotationResolver getResolver() { return resolver; } /** * Returns the {@code AnnotationResolver} to use to acquire annotations from intercepted * methods at runtime. The annotation is then used by the {@link #getHandler handler} to * perform authorization logic. * * @param resolver the {@code AnnotationResolver} to use to acquire annotations from intercepted * methods at runtime. * @since 1.1 */ public void setResolver(AnnotationResolver resolver) { this.resolver = resolver; } /** * Returns <code>true</code> if this interceptor supports, that is, should inspect, the specified * <code>MethodInvocation</code>, <code>false</code> otherwise. * <p/> * The default implementation simply does the following: * <p/> * <code>return {@link #getAnnotation(MethodInvocation) getAnnotation(mi)} != null</code> * * @param mi the <code>MethodInvocation</code> for the method being invoked. * @return <code>true</code> if this interceptor supports, that is, should inspect, the specified * <code>MethodInvocation</code>, <code>false</code> otherwise. * * 判断目标方法是否有权限注解 */ public boolean supports(MethodInvocation mi) { return getAnnotation(mi) != null; } /** * Returns the Annotation that this interceptor will process for the specified method invocation. * <p/> * The default implementation acquires the annotation using an annotation * {@link #getResolver resolver} using the internal annotation {@link #getHandler handler}'s * {@link org.apache.shiro.aop.AnnotationHandler#getAnnotationClass() annotationClass}. * * @param mi the MethodInvocation wrapping the Method from which the Annotation will be acquired. * @return the Annotation that this interceptor will process for the specified method invocation. * * 使用注解解析器获得注解,后文详解#2 */ protected Annotation getAnnotation(MethodInvocation mi) { return getResolver().getAnnotation(mi, getHandler().getAnnotationClass()); } }
MethodInterceptorSupport
提供用户登入的信息和用户所拥有的权限信息,这样其子类则具备了登入会员的信息以判断会员是否具有权限
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.shiro.aop; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; /** * This class is an abstraction of AOP method interceptor behavior specific to Shiro that * leaves AOP implementation specifics to be handled by subclass implementations. This implementation primarily * enables a <tt>Log</tt> and makes available the currently executing {@link Subject Subject}. * * @since 0.2 */ public abstract class MethodInterceptorSupport implements MethodInterceptor { /** * Default no-argument constructor for subclasses. */ public MethodInterceptorSupport() { } /** * Returns the {@link Subject Subject} associated with the currently-executing code. * <p/> * This default implementation merely calls <code>{@link org.apache.shiro.SecurityUtils#getSubject SecurityUtils.getSubject()}</code>. * * @return the {@link org.apache.shiro.subject.Subject Subject} associated with the currently-executing code. */ protected Subject getSubject() { return SecurityUtils.getSubject(); } }
RoleAnnotationMethodInterceptor
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.shiro.authz.aop; import org.apache.shiro.aop.AnnotationResolver; import org.apache.shiro.authz.annotation.RequiresRoles; /** * Checks to see if a @{@link RequiresRoles RequiresRoles} annotation is declared, and if so, performs * a role check to see if the calling <code>Subject</code> is allowed to invoke the method. * * @since 0.9 */ public class RoleAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor { /** * Default no-argument constructor that ensures this interceptor looks for * {@link RequiresRoles RequiresRoles} annotations in a method declaration. * * 创建角色注解处理器提供给父类 */ public RoleAnnotationMethodInterceptor() { super( new RoleAnnotationHandler() ); } /** * @param resolver * @since 1.1 * * 注入注解解析器如SpringAnnotationResolver,创建角色注解处理器提供给父类 */ public RoleAnnotationMethodInterceptor(AnnotationResolver resolver) { super(new RoleAnnotationHandler(), resolver); } }
书接前文#1
getHandler()获得如RoleAnnotationHandler然后调用assertAuthorized(Annotation a)校验会员是否是某个角色
RoleAnnotationHandler
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.shiro.authz.aop; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresRoles; import java.lang.annotation.Annotation; import java.util.Arrays; /** * Checks to see if a @{@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles} annotation is declared, and if so, performs * a role check to see if the calling <code>Subject</code> is allowed to proceed. * * @since 0.9.0 */ public class RoleAnnotationHandler extends AuthorizingAnnotationHandler { /** * Default no-argument constructor that ensures this handler looks for * {@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles} annotations. * * 提供RequiresRoles注解的Class信息给父类 */ public RoleAnnotationHandler() { super(RequiresRoles.class); } /** * Ensures that the calling <code>Subject</code> has the Annotation's specified roles, and if not, throws an * <code>AuthorizingException</code> indicating that access is denied. * * @param a the RequiresRoles annotation to use to check for one or more roles * @throws org.apache.shiro.authz.AuthorizationException * if the calling <code>Subject</code> does not have the role(s) necessary to * proceed. * * 核心校验,判断会员是否是某个角色 */ public void assertAuthorized(Annotation a) throws AuthorizationException { if (!(a instanceof RequiresRoles)) return; RequiresRoles rrAnnotation = (RequiresRoles) a; String[] roles = rrAnnotation.value(); if (roles.length == 1) { // 使用Subject判断会员是否是某个角色,面向对象编程,会员自己拥有校验是否是某个角色的行为 getSubject().checkRole(roles[0]); return; } if (Logical.AND.equals(rrAnnotation.logical())) { getSubject().checkRoles(Arrays.asList(roles)); return; } if (Logical.OR.equals(rrAnnotation.logical())) { // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first boolean hasAtLeastOneRole = false; for (String role : roles) if (getSubject().hasRole(role)) hasAtLeastOneRole = true; // Cause the exception if none of the role match, note that the exception message will be a bit misleading if (!hasAtLeastOneRole) getSubject().checkRole(roles[0]); } } }
书接前文#2
getResolver()获得SpringAnnotationResolver,然后调用getAnnotation(MethodInvocation mi, Annotation a)获得注解信息
SpringAnnotationResolver
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.shiro.spring.aop; import org.apache.shiro.aop.AnnotationResolver; import org.apache.shiro.aop.MethodInvocation; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ClassUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Method; /** * {@code AnnotationResolver} implementation that uses Spring's more robust * {@link AnnotationUtils AnnotationUtils} to find method annotations instead of the JDKs simpler * (and rather lacking) {@link Method}.{@link Method#getAnnotation(Class) getAnnotation(class)} * implementation. * * @since 1.1 */ public class SpringAnnotationResolver implements AnnotationResolver { public Annotation getAnnotation(MethodInvocation mi, Class<? extends Annotation> clazz) { Method m = mi.getMethod(); Annotation a = AnnotationUtils.findAnnotation(m, clazz); if (a != null) return a; //The MethodInvocation's method object could be a method defined in an interface. //However, if the annotation existed in the interface's implementation (and not //the interface itself), it won't be on the above method object. Instead, we need to //acquire the method representation from the targetClass and check directly on the //implementation itself: Class<?> targetClass = mi.getThis().getClass(); m = ClassUtils.getMostSpecificMethod(m, targetClass); a = AnnotationUtils.findAnnotation(m, clazz); if (a != null) return a; // See if the class has the same annotation return AnnotationUtils.findAnnotation(mi.getThis().getClass(), clazz); } }