001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.shiro.spring.security.interceptor;
020
021import org.apache.shiro.authz.annotation.RequiresAuthentication;
022import org.apache.shiro.authz.annotation.RequiresGuest;
023import org.apache.shiro.authz.annotation.RequiresPermissions;
024import org.apache.shiro.authz.annotation.RequiresRoles;
025import org.apache.shiro.authz.annotation.RequiresUser;
026import org.apache.shiro.mgt.SecurityManager;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
030import org.springframework.core.annotation.AnnotationUtils;
031
032import java.lang.annotation.Annotation;
033import java.lang.reflect.Method;
034
035/**
036 * TODO - complete JavaDoc
037 *
038 * @since 0.1
039 */
040@SuppressWarnings({"unchecked"})
041public class AuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {
042
043    private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationAttributeSourceAdvisor.class);
044
045    private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
046            new Class[] {
047                    RequiresPermissions.class, RequiresRoles.class,
048                    RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
049            };
050
051    protected SecurityManager securityManager;
052
053    /**
054     * Create a new AuthorizationAttributeSourceAdvisor.
055     */
056    public AuthorizationAttributeSourceAdvisor() {
057        setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor());
058    }
059
060    public SecurityManager getSecurityManager() {
061        return securityManager;
062    }
063
064    public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) {
065        this.securityManager = securityManager;
066    }
067
068    /**
069     * Returns <tt>true</tt> if the method or the class has any Shiro annotations, false otherwise.
070     * The annotations inspected are:
071     * <ul>
072     * <li>{@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication}</li>
073     * <li>{@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser}</li>
074     * <li>{@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}</li>
075     * <li>{@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles}</li>
076     * <li>{@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions}</li>
077     * </ul>
078     *
079     * @param method      the method to check for a Shiro annotation
080     * @param targetClass the class potentially declaring Shiro annotations
081     * @return <tt>true</tt> if the method has a Shiro annotation, false otherwise.
082     * @see org.springframework.aop.MethodMatcher#matches(java.lang.reflect.Method, Class)
083     */
084    public boolean matches(Method method, Class targetClass) {
085        Method m = method;
086
087        if (isAuthzAnnotationPresent(m)) {
088            return true;
089        }
090
091        //The 'method' parameter could be from an interface that doesn't have the annotation.
092        //Check to see if the implementation has it.
093        if (targetClass != null) {
094            try {
095                m = targetClass.getMethod(m.getName(), m.getParameterTypes());
096                return isAuthzAnnotationPresent(m) || isAuthzAnnotationPresent(targetClass);
097            } catch (NoSuchMethodException ignored) {
098                //default return value is false.  If we can't find the method, then obviously
099                //there is no annotation, so just use the default return value.
100            }
101        }
102
103        return false;
104    }
105
106    private boolean isAuthzAnnotationPresent(Class<?> targetClazz) {
107        for (Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES) {
108            Annotation a = AnnotationUtils.findAnnotation(targetClazz, annClass);
109            if (a != null) {
110                return true;
111            }
112        }
113        return false;
114    }
115
116    private boolean isAuthzAnnotationPresent(Method method) {
117        for (Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES) {
118            Annotation a = AnnotationUtils.findAnnotation(method, annClass);
119            if (a != null) {
120                return true;
121            }
122        }
123        return false;
124    }
125
126}