Package com.jfinal.ext.plugin.shiro

Source Code of com.jfinal.ext.plugin.shiro.ShiroPlugin

/**
* Copyright (c) 2011-2013, dafei 李飞 (myaniu AT gmail DOT com)
*
* Licensed 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 com.jfinal.ext.plugin.shiro;

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

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 com.jfinal.config.Routes;
import com.jfinal.core.ActionKey;
import com.jfinal.core.Controller;
import com.jfinal.plugin.IPlugin;

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

  private static final String SLASH = "/";

  /**
   * Shiro的几种访问控制注解
   */
  private static final Class<? extends Annotation>[] 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<String> excludedMethodName = buildExcludedMethodName();
    ConcurrentMap<String, AuthzHandler> authzMaps = new ConcurrentHashMap<String, AuthzHandler>();
    //逐个访问所有注册的Controller,解析Controller及action上的所有Shiro注解。
    //并依据这些注解,actionKey提前构建好权限检查处理器。
    for (Entry<String, Class<? extends Controller>> entry : routes
        .getEntrySet()) {
      Class<? extends Controller> controllerClass = entry.getValue();

      String controllerKey = entry.getKey();

      // 获取Controller的所有Shiro注解。
      List<Annotation> 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<Annotation> 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<String> buildExcludedMethodName() {
    Set<String> excludedMethodName = new HashSet<String>();
    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<Annotation> controllerAnnotations,
      List<Annotation> methodAnnotations) {

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

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

    // 去除空值
    List<AuthzHandler> finalAuthzHandlers = new ArrayList<AuthzHandler>();
    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
   *
   * @param authzArray
   * @param annotations
   */
  private void scanAnnotation(List<AuthzHandler> authzArray,
      List<Annotation> 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中的实现。
   *
   * @param controllerClass
   * @param method
   * @param controllerKey
   * @return
   */
  private String createActionKey(Class<? extends Controller> 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 ("".equals(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;
  }

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

  /**
   * 返回该Controller的所有访问控制注解
   *
   * @param method
   * @return
   */
  private List<Annotation> getAuthzAnnotations(
      Class<? extends Controller> targetClass) {
    List<Annotation> annotations = new ArrayList<Annotation>();
    for (Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES) {
      Annotation a = targetClass.getAnnotation(annClass);
      if (a != null) {
        annotations.add(a);
      }
    }
    return annotations;
  }
  /**
   * 该方法上是否有ClearShiro注解
   * @param method
   * @return
   */
  private boolean isClearShiroAnnotationPresent(Method method) {
    Annotation a = method.getAnnotation(ClearShiro.class);
    if (a != null) {
      return true;
    }
    return false;
  }
}
TOP

Related Classes of com.jfinal.ext.plugin.shiro.ShiroPlugin

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.