package org.springblade.common.utils;
|
|
|
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
|
import org.apache.http.HttpEntity;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.util.EntityUtils;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
|
import javax.servlet.http.HttpServletRequest;
|
import java.io.IOException;
|
import java.nio.charset.StandardCharsets;
|
import java.time.DateTimeException;
|
import java.time.Duration;
|
import java.time.Instant;
|
|
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;
|
|
/**
|
* @author xy-peng
|
*/
|
public class WechatPay2ValidatorForRequest {
|
|
protected static final Logger log = LoggerFactory.getLogger(WechatPay2ValidatorForRequest.class);
|
/**
|
* 应答超时时间,单位为分钟
|
*/
|
protected static final long RESPONSE_EXPIRED_MINUTES = 5;
|
protected final Verifier verifier;
|
protected final String requestId;
|
protected final String body;
|
|
|
public WechatPay2ValidatorForRequest(Verifier verifier, String requestId, String body) {
|
this.verifier = verifier;
|
this.requestId = requestId;
|
this.body = body;
|
}
|
|
protected static IllegalArgumentException parameterError(String message, Object... args) {
|
message = String.format(message, args);
|
return new IllegalArgumentException("parameter error: " + message);
|
}
|
|
protected static IllegalArgumentException verifyFail(String message, Object... args) {
|
message = String.format(message, args);
|
return new IllegalArgumentException("signature verify fail: " + message);
|
}
|
|
public final boolean validate(HttpServletRequest request) throws IOException {
|
try {
|
//处理请求参数
|
validateParameters(request);
|
|
//构造验签名串
|
String message = buildMessage(request);
|
|
String serial = request.getHeader(WECHAT_PAY_SERIAL);
|
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
|
|
//验签
|
if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
|
throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
|
serial, message, signature, requestId);
|
}
|
} catch (IllegalArgumentException e) {
|
log.warn(e.getMessage());
|
return false;
|
}
|
|
return true;
|
}
|
|
protected final void validateParameters(HttpServletRequest request) {
|
|
// NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
|
String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
|
|
String header = null;
|
for (String headerName : headers) {
|
header = request.getHeader(headerName);
|
if (header == null) {
|
throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
|
}
|
}
|
|
//判断请求是否过期
|
String timestampStr = header;
|
try {
|
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
|
// 拒绝过期请求
|
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
|
throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
|
}
|
} catch (DateTimeException | NumberFormatException e) {
|
throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
|
}
|
}
|
|
protected final String buildMessage(HttpServletRequest request) throws IOException {
|
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
|
String nonce = request.getHeader(WECHAT_PAY_NONCE);
|
return timestamp + "\n"
|
+ nonce + "\n"
|
+ body + "\n";
|
}
|
|
protected final String getResponseBody(CloseableHttpResponse response) throws IOException {
|
HttpEntity entity = response.getEntity();
|
return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
|
}
|
|
}
|