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

com.gitee.easyopen.ApiValidator Maven / Gradle / Ivy

package com.gitee.easyopen;

import com.gitee.easyopen.exception.BusinessParamException;
import com.gitee.easyopen.message.ErrorFactory;
import com.gitee.easyopen.message.Errors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import java.io.IOException;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 负责校验,校验工作都在这里
 * 
 * @author tanghc
 *
 */
@Slf4j
public class ApiValidator implements Validator {

    private final List SYSTEM_PACKAGE_LIST = Arrays.asList("java.lang", "java.math", "java.util", "sun.util");

    private javax.validation.Validator validator;

    public ApiValidator() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Override
    public void validate(ApiParam param) {
        if (ApiContext.getApiConfig().isIgnoreValidate() || param.fatchIgnoreValidate()) {
            log.debug("忽略所有验证(ignoreValidate=true), name:{}, version:{}", param.fatchName(), param.fatchVersion());
            return;
        }
        Assert.notNull(ApiContext.getApiConfig().getAppSecretManager(), "appSecretManager未初始化");
        
        if (param.fatchIgnoreSign()) {
            log.debug("忽略签名验证, name:{}, version:{}", param.fatchName(), param.fatchVersion());
        } else {
            // 需要验证签名
            checkAppKey(param);
            checkSign(param);
        }
        checkUploadFile(param);
        checkTimeout(param);
    }
    
    /**
     * 校验上传文件内容
     * @param param
     */
    protected void checkUploadFile(ApiParam param) {
        UploadContext uploadContext = ApiContext.getUploadContext();
        if(uploadContext != null) {
            try {
                List files = uploadContext.getAllFile();
                for (MultipartFile file : files) {
                    // 客户端传来的文件md5
                    String clientMd5 = param.getString(file.getName());
                    if(clientMd5 != null) {
                        String fileMd5 = DigestUtils.md5Hex(file.getBytes());
                        if(!clientMd5.equals(fileMd5)) {
                            throw Errors.ERROR_UPLOAD_FILE.getException();
                        }
                    }
                }
            }catch (IOException e) {
                log.error("验证上传文件MD5错误", e);
                throw Errors.ERROR_UPLOAD_FILE.getException();
            }
        }
        
    }

    protected void checkTimeout(ApiParam param) {
        int timeoutSeconds = ApiContext.getApiConfig().getTimeoutSeconds();
        // 如果设置为0,表示不校验
        if(timeoutSeconds == 0) {
            return;
        }
        if (timeoutSeconds < 0) {
            throw new IllegalArgumentException("服务端timeoutSeconds设置错误");
        }
        String requestTime = param.fatchTimestamp();
        try {
            Date requestDate = new SimpleDateFormat(ParamNames.TIMESTAMP_PATTERN).parse(requestTime);
            long requestMilliseconds = requestDate.getTime();
            if (System.currentTimeMillis() - requestMilliseconds > timeoutSeconds * 1000) {
                throw Errors.TIMEOUT.getException(param.fatchNameVersion(), timeoutSeconds);
            }
        } catch (ParseException e) {
            throw Errors.TIME_INVALID.getException(param.fatchNameVersion());
        }
    }

    protected void checkAppKey(ApiParam param) {
        Assert.notNull(ApiContext.getApiConfig().getAppSecretManager(), "appSecretManager未初始化");
        if (StringUtils.isEmpty(param.fatchAppKey())) {
            throw Errors.NO_APP_ID.getException(param.fatchNameVersion(), ParamNames.APP_KEY_NAME);
        }
        boolean isTrueAppKey = ApiContext.getApiConfig().getAppSecretManager().isValidAppKey(param.fatchAppKey());
        if (!isTrueAppKey) {
            throw Errors.ERROR_APP_ID.getException(param.fatchNameVersion(), ParamNames.APP_KEY_NAME);
        }
    }

    protected void checkSign(ApiParam param) {
        if (StringUtils.isEmpty(param.fatchSign())) {
            throw Errors.NO_SIGN_PARAM.getException(param.fatchNameVersion(), ParamNames.SIGN_NAME);
        }
        String secret = ApiContext.getApiConfig().getAppSecretManager().getSecret(param.fatchAppKey());
        
        Signer signer = ApiContext.getApiConfig().getSigner();
        boolean isRightSign = signer.isRightSign(param, secret, param.fatchSignMethod());
        // 错误的sign
        if(!isRightSign) {
            throw Errors.ERROR_SIGN.getException(param.fatchNameVersion());
        }
    }
    
    
    @Override
    public void validateBusiParam(Object obj) {
        if(obj == null) {
            return;
        }
        // 先校验属性对象
        List fields = listObjectField(obj);
        if (!fields.isEmpty()) {
            fields.forEach(this::validateBusiParam);
        }
        Set> set = validator.validate(obj);
        if (CollectionUtils.isNotEmpty(set)) {
            ConstraintViolation oneError = set.iterator().next();
            String errorMsg = oneError.getMessage();
            throw this.getValidateBusiParamException(errorMsg);
        }
    }

    private List listObjectField(Object object) {
        List ret = new ArrayList<>();
        ReflectionUtils.doWithFields(object.getClass(), field -> {
            ReflectionUtils.makeAccessible(field);
            ret.add(field.get(object));
        }, this::isMatchField);
        return ret;
    }

    /**
     * 匹配校验字段。
     *
     * 1. 不为基本类型;
     * 2. 不为java自带的类型;
     * 3. 不为枚举
     * 4. 不为Map
     * @param field field
     * @return true,是自定义的
     */
    private boolean isMatchField(Field field) {
        Class fieldType = field.getType();
        if (fieldType.isPrimitive()) {
            return false;
        }
        if (Map.class.isAssignableFrom(fieldType)) {
            return false;
        }
        Class declaringClass = field.getDeclaringClass();
        boolean isSame = declaringClass == fieldType;
        boolean isAssignableFrom = declaringClass.isAssignableFrom(fieldType)
                || fieldType.isAssignableFrom(declaringClass);
        // 如果是相同类,或者有继承关系不校验。
        if (isSame || isAssignableFrom) {
            return false;
        }
        Package aPackage = fieldType.getPackage();
        if (aPackage == null) {
            return false;
        }
        String packageName = aPackage.getName();
        for (String prefix : SYSTEM_PACKAGE_LIST) {
            if (packageName.startsWith(prefix)) {
                return false;
            }
        }
        return true;
    }

    private RuntimeException getValidateBusiParamException(String errorMsg) {
        String code = Errors.BUSI_PARAM_ERROR.getCode();
        String[] msgToken = errorMsg.split("=");
        String msg = msgToken[0];
        if (msg.startsWith("{") && msg.endsWith("}")) {
            String module = msg.substring(1, msg.length() - 1);
            Object[] params = this.buildParams(msgToken);
            String error = ErrorFactory.getErrorMessage(module, ApiContext.getLocal(), params);
            return new BusinessParamException(error, code);
        } else {
            return new BusinessParamException(errorMsg, code);
        }
    }

    private Object[] buildParams(String[] msgToken) {
        if (msgToken.length == 2) {
            return msgToken[1].split(",");
        } else {
            return new Object[0];
        }
    }

    public javax.validation.Validator getValidator() {
        return validator;
    }

    public void setValidator(javax.validation.Validator validator) {
        this.validator = validator;
    }

}