/**
*
* 日 期:12-2-13
*/
package com.rop.security;
import com.rop.*;
import com.rop.annotation.HttpAction;
import com.rop.config.SystemParameterNames;
import com.rop.impl.DefaultServiceAccessController;
import com.rop.impl.SimpleRopRequestContext;
import com.rop.request.UploadFileUtils;
import com.rop.session.SessionManager;
import com.rop.utils.RopUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import java.util.*;
/**
* @author 陈雄华
* @version 1.0
*/
public class DefaultSecurityManager implements SecurityManager {
protected Logger logger = LoggerFactory.getLogger(getClass());
protected ServiceAccessController serviceAccessController = new DefaultServiceAccessController();
protected AppSecretManager appSecretManager = new FileBaseAppSecretManager();
protected SessionManager sessionManager;
protected InvokeTimesController invokeTimesController;
protected FileUploadController fileUploadController;
private static final Map<String, SubErrorType> INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS = new LinkedHashMap<String, SubErrorType>();
static {
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("typeMismatch", SubErrorType.ISV_PARAMETERS_MISMATCH);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("NotNull", SubErrorType.ISV_MISSING_PARAMETER);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("NotEmpty", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("Size", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("Range", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("Pattern", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("Min", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("Max", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("DecimalMin", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("DecimalMax", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("Digits", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("Past", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("Future", SubErrorType.ISV_INVALID_PARAMETE);
INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.put("AssertFalse", SubErrorType.ISV_INVALID_PARAMETE);
}
public MainError validateSystemParameters(RopRequestContext context) {
RopContext ropContext = context.getRopContext();
MainError mainError = null;
//1.检查appKey
if (context.getAppKey() == null) {
return MainErrors.getError(MainErrorType.MISSING_APP_KEY, context.getLocale(),
context.getMethod(),context.getVersion(),
SystemParameterNames.getAppKey());
}
if (!appSecretManager.isValidAppKey(context.getAppKey())) {
return MainErrors.getError(MainErrorType.INVALID_APP_KEY, context.getLocale(),
context.getMethod(),context.getVersion(),
context.getAppKey());
}
//2.检查会话
mainError = checkSession(context);
if (mainError != null) {
return mainError;
}
//3.检查method参数
if (context.getMethod() == null) {
return MainErrors.getError(MainErrorType.MISSING_METHOD, context.getLocale(),
SystemParameterNames.getMethod());
} else {
if (!ropContext.isValidMethod(context.getMethod())) {
return MainErrors.getError(MainErrorType.INVALID_METHOD,
context.getLocale(),context.getMethod());
}
}
//4.检查v参数
if (context.getVersion() == null) {
return MainErrors.getError(MainErrorType.MISSING_VERSION, context.getLocale(),
context.getMethod(),
SystemParameterNames.getVersion());
} else {
if (!ropContext.isValidVersion(context.getMethod(), context.getVersion())) {
return MainErrors.getError(
MainErrorType.UNSUPPORTED_VERSION, context.getLocale(),
context.getMethod(), context.getVersion());
}
}
//5.检查签名正确性
mainError = checkSign(context);
if (mainError != null) {
return mainError;
}
//6.检查服务方法的版本是否已经过期
if (context.getServiceMethodDefinition().isObsoleted()) {
return MainErrors.getError(MainErrorType.METHOD_OBSOLETED, context.getLocale(),
context.getMethod(), context.getVersion());
}
//7.检查请求HTTP方法的匹配性
mainError = validateHttpAction(context);
if (mainError != null) {
return mainError;
}
//8.检查 format
if (!MessageFormat.isValidFormat(context.getFormat())) {
return MainErrors.getError(MainErrorType.INVALID_FORMAT, context.getLocale(),
context.getMethod(),context.getVersion(),context.getFormat());
}
return null;
}
public MainError validateOther(RopRequestContext rrctx) {
MainError mainError = null;
//1.判断应用/用户是否有权访问目标服务
mainError = checkServiceAccessAllow(rrctx);
if (mainError != null) {
return mainError;
}
//2.判断应用/会话/用户访问服务的次数或频度是否超限
mainError = checkInvokeTimesLimit(rrctx);
if (mainError != null) {
return mainError;
}
//3.如果是上传文件的服务,检查文件类型和大小是否满足要求
mainError = checkUploadFile(rrctx);
if (mainError != null) {
return mainError;
}
//4.检查业务参数合法性
mainError = validateBusinessParams(rrctx);
if (mainError != null) {
return mainError;
}
return null;
}
private MainError checkUploadFile(RopRequestContext rrctx) {
ServiceMethodHandler serviceMethodHandler = rrctx.getServiceMethodHandler();
if (serviceMethodHandler != null && serviceMethodHandler.hasUploadFiles()) {
List<String> fileFieldNames = serviceMethodHandler.getUploadFileFieldNames();
for (String fileFieldName : fileFieldNames) {
String paramValue = rrctx.getParamValue(fileFieldName);
if (paramValue != null) {
if (paramValue.indexOf("@") < 0) {
return MainErrors.getError(
MainErrorType.UPLOAD_FAIL, rrctx.getLocale(),
rrctx.getMethod(), rrctx.getVersion(), "MESSAGE_VALID:not contain '@'.");
} else {
String fileType = UploadFileUtils.getFileType(paramValue);
if (!fileUploadController.isAllowFileType(fileType)) {
return MainErrors.getError(
MainErrorType.UPLOAD_FAIL, rrctx.getLocale(),
rrctx.getMethod(), rrctx.getVersion(),
"FILE_TYPE_NOT_ALLOW:the valid file types is:" + fileUploadController.getAllowFileTypes());
}
byte[] fileContent = UploadFileUtils.decode(paramValue);
if (fileUploadController.isExceedMaxSize(fileContent.length)) {
return MainErrors.getError(
MainErrorType.UPLOAD_FAIL, rrctx.getLocale(),
rrctx.getMethod(), rrctx.getVersion(),
"EXCEED_MAX_SIZE:" + fileUploadController.getMaxSize() + "k");
}
}
}
}
}
return null;
}
public void setInvokeTimesController(InvokeTimesController invokeTimesController) {
this.invokeTimesController = invokeTimesController;
}
public void setServiceAccessController(ServiceAccessController serviceAccessController) {
this.serviceAccessController = serviceAccessController;
}
public void setAppSecretManager(AppSecretManager appSecretManager) {
this.appSecretManager = appSecretManager;
}
public void setSessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
public void setFileUploadController(FileUploadController fileUploadController) {
this.fileUploadController = fileUploadController;
}
private MainError checkInvokeTimesLimit(RopRequestContext rrctx) {
if (invokeTimesController.isAppInvokeFrequencyExceed(rrctx.getAppKey())) {
return MainErrors.getError(MainErrorType.EXCEED_APP_INVOKE_FREQUENCY_LIMITED, rrctx.getLocale());
} else if (invokeTimesController.isAppInvokeLimitExceed(rrctx.getAppKey())) {
return MainErrors.getError(MainErrorType.EXCEED_APP_INVOKE_LIMITED, rrctx.getLocale());
} else if (invokeTimesController.isSessionInvokeLimitExceed(rrctx.getAppKey(), rrctx.getSessionId())) {
return MainErrors.getError(MainErrorType.EXCEED_SESSION_INVOKE_LIMITED, rrctx.getLocale());
} else if (invokeTimesController.isUserInvokeLimitExceed(rrctx.getAppKey(), rrctx.getSession())) {
return MainErrors.getError(MainErrorType.EXCEED_USER_INVOKE_LIMITED, rrctx.getLocale());
} else {
return null;
}
}
/**
* 校验是否是合法的HTTP动作
*
* @param ropRequestContext
*/
private MainError validateHttpAction(RopRequestContext ropRequestContext) {
MainError mainError = null;
HttpAction[] httpActions = ropRequestContext.getServiceMethodDefinition().getHttpAction();
if (httpActions.length > 0) {
boolean isValid = false;
for (HttpAction httpAction : httpActions) {
if (httpAction == ropRequestContext.getHttpAction()) {
isValid = true;
break;
}
}
if (!isValid) {
mainError = MainErrors.getError(
MainErrorType.HTTP_ACTION_NOT_ALLOWED, ropRequestContext.getLocale(),
ropRequestContext.getMethod(), ropRequestContext.getVersion(),
ropRequestContext.getHttpAction());
}
}
return mainError;
}
public ServiceAccessController getServiceAccessController() {
return serviceAccessController;
}
public AppSecretManager getAppSecretManager() {
return appSecretManager;
}
private MainError checkServiceAccessAllow(RopRequestContext smc) {
if (!getServiceAccessController().isAppGranted(smc.getAppKey(), smc.getMethod(), smc.getVersion())) {
MainError mainError = SubErrors.getMainError(SubErrorType.ISV_INVALID_PERMISSION, smc.getLocale());
SubError subError = SubErrors.getSubError(SubErrorType.ISV_INVALID_PERMISSION.value(),
SubErrorType.ISV_INVALID_PERMISSION.value(),
smc.getLocale());
mainError.addSubError(subError);
if (mainError != null && logger.isErrorEnabled()) {
logger.debug("未向ISV开放该服务的执行权限(" + smc.getMethod() + ")");
}
return mainError;
} else {
if (!getServiceAccessController().isUserGranted(smc.getSession(), smc.getMethod(), smc.getVersion())) {
MainError mainError = MainErrors.getError(
MainErrorType.INSUFFICIENT_USER_PERMISSIONS, smc.getLocale(),
smc.getMethod(), smc.getVersion());
SubError subError = SubErrors.getSubError(SubErrorType.ISV_INVALID_PERMISSION.value(),
SubErrorType.ISV_INVALID_PERMISSION.value(),
smc.getLocale());
mainError.addSubError(subError);
if (mainError != null && logger.isErrorEnabled()) {
logger.debug("未向会话用户开放该服务的执行权限(" + smc.getMethod() + ")");
}
return mainError;
}
return null;
}
}
private MainError validateBusinessParams(RopRequestContext context) {
List<ObjectError> errorList =
(List<ObjectError>) context.getAttribute(SimpleRopRequestContext.SPRING_VALIDATE_ERROR_ATTRNAME);
//将Bean数据绑定时产生的错误转换为Rop的错误
if (errorList != null && errorList.size() > 0) {
return toMainErrorOfSpringValidateErrors(errorList, context.getLocale(),context);
} else {
return null;
}
}
/**
* 检查签名的有效性
*
* @param context
* @return
*/
private MainError checkSign(RopRequestContext context) {
//系统级签名开启,且服务方法需求签名
if (context.isSignEnable()) {
if (!context.getServiceMethodDefinition().isIgnoreSign()) {
if (context.getSign() == null) {
return MainErrors.getError(MainErrorType.MISSING_SIGNATURE, context.getLocale(),
context.getMethod(),context.getVersion(),
SystemParameterNames.getSign());
} else {
//获取需要签名的参数
List<String> ignoreSignFieldNames = context.getServiceMethodHandler().getIgnoreSignFieldNames();
HashMap<String, String> needSignParams = new HashMap<String, String>();
for (Map.Entry<String, String> entry : context.getAllParams().entrySet()) {
if (!ignoreSignFieldNames.contains(entry.getKey())) {
needSignParams.put(entry.getKey(), entry.getValue());
}
}
//查看密钥是否存在,不存在则说明appKey是非法的
String signSecret = getAppSecretManager().getSecret(context.getAppKey());
if (signSecret == null) {
throw new RopException("无法获取" + context.getAppKey() + "对应的密钥");
}
String signValue = RopUtils.sign(needSignParams, signSecret);
if (!signValue.equals(context.getSign())) {
if (logger.isErrorEnabled()) {
logger.error(context.getAppKey() + "的签名不合法,请检查");
}
return MainErrors.getError(
MainErrorType.INVALID_SIGNATURE, context.getLocale(),
context.getMethod(),context.getVersion());
} else {
return null;
}
}
} else {
if (logger.isWarnEnabled()) {
logger.warn(context.getMethod() + "忽略了签名");
}
return null;
}
} else {
if (logger.isDebugEnabled()) {
logger.warn("{}{}服务方法未开启签名", context.getMethod(), context.getVersion());
}
return null;
}
}
/**
* 是否是合法的会话
*
* @param context
* @return
*/
private MainError checkSession(RopRequestContext context) {
//需要进行session检查
if (context.getServiceMethodHandler() != null &&
context.getServiceMethodHandler().getServiceMethodDefinition().isNeedInSession()) {
if (context.getSessionId() == null) {
return MainErrors.getError(MainErrorType.MISSING_SESSION, context.getLocale(),
context.getMethod(), context.getVersion(), SystemParameterNames.getSessionId());
} else {
if (!isValidSession(context)) {
return MainErrors.getError(MainErrorType.INVALID_SESSION, context.getLocale(),
context.getMethod(), context.getVersion(),context.getSessionId());
}
}
}
return null;
}
private boolean isValidSession(RopRequestContext smc) {
if (sessionManager.getSession(smc.getSessionId()) == null) {
if (logger.isDebugEnabled()) {
logger.debug(smc.getSessionId() + "会话不存在,请检查。");
}
return false;
} else {
return true;
}
}
/**
* 将通过JSR 303框架校验的错误转换为Rop的错误体系
*
* @param allErrors
* @param locale
* @return
*/
private MainError toMainErrorOfSpringValidateErrors(
List<ObjectError> allErrors, Locale locale,RopRequestContext context) {
if (hastSubErrorType(allErrors, SubErrorType.ISV_MISSING_PARAMETER)) {
return getBusinessParameterMainError(allErrors, locale, SubErrorType.ISV_MISSING_PARAMETER,context);
} else if (hastSubErrorType(allErrors, SubErrorType.ISV_PARAMETERS_MISMATCH)) {
return getBusinessParameterMainError(allErrors, locale, SubErrorType.ISV_PARAMETERS_MISMATCH,context);
} else {
return getBusinessParameterMainError(allErrors, locale, SubErrorType.ISV_INVALID_PARAMETE,context);
}
}
/**
* 判断错误列表中是否包括指定的子错误
*
* @param allErrors
* @param subErrorType1
* @return
*/
private boolean hastSubErrorType(List<ObjectError> allErrors, SubErrorType subErrorType1) {
for (ObjectError objectError : allErrors) {
if (objectError instanceof FieldError) {
FieldError fieldError = (FieldError) objectError;
if (INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.containsKey(fieldError.getCode())) {
SubErrorType tempSubErrorType = INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.get(fieldError.getCode());
if (tempSubErrorType == subErrorType1) {
return true;
}
}
}
}
return false;
}
/**
* 生成对应子错误的错误类
*
* @param allErrors
* @param locale
* @param subErrorType
* @return
*/
private MainError getBusinessParameterMainError(
List<ObjectError> allErrors, Locale locale, SubErrorType subErrorType,RopRequestContext context) {
MainError mainError = SubErrors.getMainError(subErrorType, locale,context.getMethod(),context.getVersion());
for (ObjectError objectError : allErrors) {
if (objectError instanceof FieldError) {
FieldError fieldError = (FieldError) objectError;
SubErrorType tempSubErrorType = INVALIDE_CONSTRAINT_SUBERROR_MAPPINGS.get(fieldError.getCode());
if (tempSubErrorType == subErrorType) {
String subErrorCode =
SubErrors.getSubErrorCode(
tempSubErrorType, fieldError.getField(), fieldError.getRejectedValue());
SubError subError = SubErrors.getSubError(subErrorCode, tempSubErrorType.value(), locale,
fieldError.getField(), fieldError.getRejectedValue());
mainError.addSubError(subError);
}
}
}
return mainError;
}
}