package com.aggrepoint.winlet.plugin;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import com.aggrepoint.utils.StringUtils;
import com.aggrepoint.winlet.AccessRuleEngine;
import com.aggrepoint.winlet.AuthorizationEngine;
import com.aggrepoint.winlet.ContextUtils;
import com.aggrepoint.winlet.site.domain.Area;
import com.aggrepoint.winlet.site.domain.Branch;
import com.aggrepoint.winlet.site.domain.Page;
import com.aggrepoint.winlet.spring.WinletClassLoader;
import com.aggrepoint.winlet.spring.annotation.AccessRule;
import com.aggrepoint.winlet.spring.annotation.Action;
import com.aggrepoint.winlet.spring.annotation.Unspecified;
import com.aggrepoint.winlet.spring.def.WinletDef;
/**
* <pre>
* 实现基于AccessRule的访问检查
* AccessRule可以标注在非Winlet Controller类和方法上
* check(path)仅适用于path指向Winlet方法的情况
* </pre>
*
* @author jiangmingyang
*/
public class AccessRuleAuthorizationEngine implements AuthorizationEngine {
static AccessRuleEngine ruleEngine;
static HashMap<Class<?>, AccessRule> rulesOnClass = new HashMap<Class<?>, AccessRule>();
static HashMap<Method, AccessRule> rulesOnMethod = new HashMap<Method, AccessRule>();
static final Log logger = LogFactory
.getLog(AccessRuleAuthorizationEngine.class);
private static AccessRule getRule(Class<?> c) {
if (rulesOnClass.containsKey(c))
return rulesOnClass.get(c);
AccessRule ar = AnnotationUtils.findAnnotation(c, AccessRule.class);
rulesOnClass.put(c, ar);
return ar;
}
protected static AccessRule getRule(Method method) {
if (rulesOnMethod.containsKey(method))
return rulesOnMethod.get(method);
AccessRule ar = AnnotationUtils
.findAnnotation(method, AccessRule.class);
rulesOnMethod.put(method, ar);
return ar;
}
protected static AccessRuleEngine getRuleEngine() {
if (ruleEngine == null) {
ruleEngine = ContextUtils.getAccessRuleEngine(ContextUtils
.getRequest());
}
return ruleEngine;
}
@Override
public Class<? extends Exception> check(Branch branch) {
if (branch.getRule() == null)
return null;
try {
if (getRuleEngine().eval(branch.getRule()))
return null;
} catch (Exception e) {
logger.error(
"Error evaluating branch access rule \"" + branch.getRule()
+ "\".", e);
}
return Unspecified.class;
}
@Override
public Class<? extends Exception> check(Page page, boolean expand) {
String rule = expand ? page.getExpandRule() : page.getRule();
if (rule == null) {
if (!expand) {
// 访问page本身,但page上没有定义访问规则。
// 如果page中有area,并且area中有引用winlet,则当前用户至少可以访问其中一个winlet才允许访问该页面
boolean hasWinlet = false;
for (Area area : page.getAreas(expand)) {
if (area.isCascade()) // cascade的area不作为判断依据
continue;
if (area.getWinletUrls().size() > 0) {
hasWinlet = true;
for (String url : area.getWinletUrls()) {
if (check(url) == null)
return null;
}
}
}
return hasWinlet ? Unspecified.class : null;
}
return null;
}
try {
if (getRuleEngine().eval(rule))
return null;
} catch (Exception e) {
logger.error("Error evaluating rule \"" + rule
+ "\" defined on page \"" + page.getFullPath() + "\".", e);
}
return Unspecified.class;
}
@Override
public Class<? extends Exception> check(Class<?> controller) {
AccessRule rule = getRule(controller);
if (rule == null)
return null;
try {
if (getRuleEngine().eval(rule.value()))
return null;
} catch (Exception e) {
logger.error("Error evaluating access rule \"" + rule.value()
+ "\" defined on class " + controller.getName() + "\"", e);
return rule.exception();
}
return rule.exception();
}
@Override
public Class<? extends Exception> check(Class<?> controller, Method method) {
AccessRule rule = getRule(method);
if (rule == null)
return check(controller);
try {
if (getRuleEngine().eval(rule.value()))
return null;
} catch (Exception e) {
logger.error("Error evaluating access rule \"" + rule.value()
+ "\" defined on method \"" + method.getName()
+ "\" of class \"" + method.getDeclaringClass().getName()
+ "\"", e);
return rule.exception();
}
return rule.exception();
}
static Map<String, Method> methodMap = Collections
.synchronizedMap(new HashMap<String, Method>());
static Map<String, Class<?>> classMap = Collections
.synchronizedMap(new HashMap<String, Class<?>>());
@Override
public Class<? extends Exception> check(String url) {
if (url == null)
return Unspecified.class;
// { 分解winlet url和method url,获得完整的URL
String winletUrl = null;
String methodUrl = null;
url = url.trim();
if (url.startsWith("/"))
url = url.substring(1);
int idx = url.indexOf("/");
if (idx > 0) {
winletUrl = "/" + url.substring(0, idx).trim();
methodUrl = "/" + url.substring(idx + 1).trim();
} else {
methodUrl = "/" + url.trim();
}
if (winletUrl == null) {
HandlerMethod hm = ContextUtils.getHandlerMethod(ContextUtils
.getRequest());
if (hm == null)
return Unspecified.class;
WinletDef def = WinletDef.getDef(hm.getBeanType());
if (def == null)
return Unspecified.class;
winletUrl = "/" + def.getName();
}
// 获得完整的URL
url = winletUrl + methodUrl;
// }
Method method = methodMap.get(url);
Class<?> clz = classMap.get(url);
if (method == null) { // 找到url对应的method
if (methodMap.containsKey(url))
return Unspecified.class;
methodMap.put(url, null);
// { 找到对应的Winlet类
clz = WinletClassLoader.getWinletClassByPath(winletUrl);
if (clz == null)
return Unspecified.class;
// }
// { 找到对应的方法
for (Method m : clz.getMethods()) {
RequestMapping rm = AnnotationUtils.findAnnotation(m,
RequestMapping.class);
if (rm != null)
for (String str : rm.value())
if (methodUrl.equals(str)) {
method = m;
break;
}
if (method != null)
break;
Action action = AnnotationUtils.findAnnotation(m, Action.class);
if (action != null) {
String str = action.value();
if (StringUtils.isEmpty(str))
str = m.getName();
str = "/" + str;
if (methodUrl.equals(str)) {
method = m;
break;
}
}
}
if (method == null)
return Unspecified.class;
// }
methodMap.put(url, method);
classMap.put(url, clz);
}
return check(clz, method);
}
}