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

com.janeluo.jfinalplus.plugin.shiro.ShiroPlugin Maven / Gradle / Ivy

There is a newer version: 2.2.0.r3
Show newest version
/**
 * 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.janeluo.jfinalplus.plugin.shiro;

import com.jfinal.config.Routes;
import com.jfinal.core.ActionKey;
import com.jfinal.core.Controller;
import com.jfinal.plugin.IPlugin;
import org.apache.shiro.authz.annotation.*;

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;

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

	private static final String SLASH = "/";

	/**
	 * 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 = new ConcurrentHashMap();
		//逐个访问所有注册的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 = new ArrayList(5);
		for (int index = 0; index < 5; index++) {
			authzHandlers.add(null);
		}

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

		// 去除空值
		List finalAuthzHandlers = new ArrayList();
		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 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中的实现。
	 *
	 * @param controllerClass
	 * @param method
	 * @param controllerKey
	 * @return
	 */
	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 ("".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 getAuthzAnnotations(Method method) {
		List annotations = new ArrayList();
		for (Class annClass : AUTHZ_ANNOTATION_CLASSES) {
			Annotation a = method.getAnnotation(annClass);
			if (a != null) {
				annotations.add(a);
			}
		}
		return annotations;
	}

	/**
	 * 返回该Controller的所有访问控制注解
	 *
	 * @param method
	 * @return
	 */
	private List getAuthzAnnotations(
			Class targetClass) {
		List annotations = new ArrayList();
		for (Class 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;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy