All Downloads are FREE. Search and download functionalities are using the official Maven repository.

goja.plugins.shiro.ShiroPlugin Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */
package goja.plugins.shiro;

import com.jfinal.config.Routes;
import com.jfinal.core.ActionKey;
import com.jfinal.core.Controller;
import com.jfinal.plugin.IPlugin;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

import static goja.core.StringPool.SLASH;

/**
 * Shiro插件,启动时加载所有Shiro访问控制注解。
 *
 * @author dafei
 */
@SuppressWarnings("unchecked")
public class ShiroPlugin implements IPlugin {

    /**
     * Shiro的几种访问控制注解
     */
    private static final Class[] AUTHZ_ANNOTATION_CLASSES = new Class[]{
            RequiresPermissions.class, RequiresRoles.class, RequiresUser.class,
            RequiresGuest.class, RequiresAuthentication.class};

    /**
     * 路由设定
     */
    private final Routes routes;

    /**
     * 构造函数
     *
     * @param routes 路由设定
     */
    public ShiroPlugin(Routes routes) {
        this.routes = routes;
    }

    /**
     * 停止插件
     */
    @Override
    public boolean stop() {
        return true;
    }

    /**
     * 启动插件
     */
    @Override
    public boolean start() {
        Set excludedMethodName = buildExcludedMethodName();
        ConcurrentMap authzMaps = Maps.newConcurrentMap();
        //逐个访问所有注册的Controller,解析Controller及action上的所有Shiro注解。
        //并依据这些注解,actionKey提前构建好权限检查处理器。
        for (Entry> entry : routes.getEntrySet()) {
            Class controllerClass = entry.getValue();

            String controllerKey = entry.getKey();

            // 获取Controller的所有Shiro注解。
            List controllerAnnotations = getAuthzAnnotations(controllerClass);
            // 逐个遍历方法。
            Method[] methods = controllerClass.getMethods();
            for (Method method : methods) {
                //排除掉Controller基类的所有方法,并且只关注没有参数的Action方法。
                if (!excludedMethodName.contains(method.getName())
                        && method.getParameterTypes().length == 0) {
                    //若该方法上存在ClearShiro注解,则对该action不进行访问控制检查。
                    if (isClearShiroAnnotationPresent(method)) {
                        continue;
                    }
                    //获取方法的所有Shiro注解。
                    List methodAnnotations = getAuthzAnnotations(method);
                    //依据Controller的注解和方法的注解来生成访问控制处理器。
                    AuthzHandler authzHandler = createAuthzHandler(controllerAnnotations, methodAnnotations);
                    //生成访问控制处理器成功。
                    if (authzHandler != null) {
                        //构建ActionKey,参考ActionMapping中实现
                        String actionKey = createActionKey(controllerClass, method, controllerKey);
                        //添加映射
                        authzMaps.put(actionKey, authzHandler);
                    }
                }
            }
        }
        //注入到ShiroKit类中。ShiroKit类以单例模式运行。
        ShiroKit.init(authzMaps);
        return true;
    }

    /**
     * 从Controller方法中构建出需要排除的方法列表
     *
     * @return 排除的方法列表
     */
    private Set buildExcludedMethodName() {
        Set excludedMethodName = new HashSet();
        Method[] methods = Controller.class.getMethods();
        for (Method m : methods) {
            if (m.getParameterTypes().length == 0) {
                excludedMethodName.add(m.getName());
            }
        }
        return excludedMethodName;
    }

    /**
     * 依据Controller的注解和方法的注解来生成访问控制处理器。
     *
     * @param controllerAnnotations Controller的注解
     * @param methodAnnotations     方法的注解
     * @return 访问控制处理器
     */
    private AuthzHandler createAuthzHandler(
            List controllerAnnotations,
            List methodAnnotations) {

        //没有注解
        if (controllerAnnotations.size() == 0 && methodAnnotations.size() == 0) {
            return null;
        }
        //至少有一个注解
        List authzHandlers = Lists.newArrayListWithCapacity(5);
        for (int index = 0; index < 5; index++) {
            authzHandlers.add(null);
        }

        // 逐个扫描注解,若是相应的注解则在相应的位置赋值。
        scanAnnotation(authzHandlers, controllerAnnotations);
        // 逐个扫描注解,若是相应的注解则在相应的位置赋值。函数的注解优先级高于Controller
        scanAnnotation(authzHandlers, methodAnnotations);

        // 去除空值
        List finalAuthzHandlers = Lists.newArrayList();
        for (AuthzHandler a : authzHandlers) {
            if (a != null) {
                finalAuthzHandlers.add(a);
            }
        }
        authzHandlers = null;
        // 存在多个,则构建组合AuthzHandler
        if (finalAuthzHandlers.size() > 1) {
            return new CompositeAuthzHandler(finalAuthzHandlers);
        }
        // 一个的话直接返回
        return finalAuthzHandlers.get(0);
    }

    /**
     * 逐个扫描注解,若是相应的注解则在相应的位置赋值。 注解的处理是有顺序的,依次为RequiresRoles,RequiresPermissions,
     * RequiresAuthentication,RequiresUser,RequiresGuest
     */
    private void scanAnnotation(List authzArray,
                                List annotations) {
        if (null == annotations || 0 == annotations.size()) {
            return;
        }
        for (Annotation a : annotations) {
            if (a instanceof RequiresRoles) {
                authzArray.set(0, new RoleAuthzHandler(a));
            } else if (a instanceof RequiresPermissions) {
                authzArray.set(1, new PermissionAuthzHandler(a));
            } else if (a instanceof RequiresAuthentication) {
                authzArray.set(2, AuthenticatedAuthzHandler.me());
            } else if (a instanceof RequiresUser) {
                authzArray.set(3, UserAuthzHandler.me());
            } else if (a instanceof RequiresGuest) {
                authzArray.set(4, GuestAuthzHandler.me());
            }
        }
    }

    /**
     * 构建actionkey,参考ActionMapping中的实现。
     */
    private String createActionKey(Class controllerClass,
                                   Method method, String controllerKey) {
        String methodName = method.getName();
        String actionKey;

        ActionKey ak = method.getAnnotation(ActionKey.class);
        if (ak != null) {
            actionKey = ak.value().trim();
            if (Strings.isNullOrEmpty(actionKey)) {
                throw new IllegalArgumentException(controllerClass.getName()
                        + "."
                        + methodName
                        + "(): The argument of ActionKey can not be blank.");
            }
            if (!actionKey.startsWith(SLASH)) {
                actionKey = SLASH + actionKey;
            }
        } else if (methodName.equals("index")) {
            actionKey = controllerKey;
        } else {
            actionKey =
                    controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
        }
        return actionKey;
    }

    /**
     * 返回该方法的所有访问控制注解
     */
    private List getAuthzAnnotations(Method method) {
        List annotations = Lists.newArrayList();
        for (Class annClass : AUTHZ_ANNOTATION_CLASSES) {
            Annotation a = method.getAnnotation(annClass);
            if (a != null) {
                annotations.add(a);
            }
        }
        return annotations;
    }

    /**
     * 返回该Controller的所有访问控制注解
     */
    private List getAuthzAnnotations(
            Class targetClass) {
        List annotations = Lists.newArrayList();
        for (Class annClass : AUTHZ_ANNOTATION_CLASSES) {
            Annotation a = targetClass.getAnnotation(annClass);
            if (a != null) {
                annotations.add(a);
            }
        }
        return annotations;
    }

    /**
     * 该方法上是否有ClearShiro注解
     */
    private boolean isClearShiroAnnotationPresent(Method method) {
        Annotation a = method.getAnnotation(ClearShiro.class);
        return a != null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy