后端接口为了安全考虑, 通常会增加接口参数签名认证;增加接口参数签名认证, postman直接调用,会认证失败, 需要实现一套和前端项目一样的签名动作;

api参数签名认证api参数签名认证原理

  • api-time: 本次API调用的有效时间,超过该时间调用失效,避免重放攻击
  • api-key: 与api-secret是一个pair对,一一对应,知道了api-key即可查询到api-secret
  • api-signature: api-secret和message一起生成的签名,这里的message一般包括api-key, 请求方法, get, post参数

api-signature的生成规则一般为: crypto-js的hmac_sha256,输出值需转化为base64字符串

postman pre-request签名计算


var apiKey = pm.globals.get("apiKey");
var apiSecret = pm.globals.get("apiSecret");

const query = pm.request.url.query;
var keys = [];
var map = {};
query?.each(e => {
    let disabled = e["disabled"];
    if (!disabled) {
        let k = e["key"];
        keys.push(k);
        map[k] = e["value"];
    }
});
const queryArr = [];
keys.sort();
keys.forEach(k => {
    queryArr.push(k + "=" + map[k]);
});
const queryStr = queryArr.join("&");
const body = JSON.stringify(JSON.parse(pm.request.body.raw.toString()));

var method = request.method;
var signArr = [apiKey, method.toLowerCase(), queryStr, body];
const signSrcStr = signArr.join('&');
console.log(signSrcStr);

var signatureBase64 = CryptoJS.HmacSHA256(signSrcStr, apiSecret).toString(CryptoJS.enc.Base64);
console.log("signatureBase64", signatureBase64);
pm.environment.set("api-signature", signatureBase64);

const timeStr = Math.round(new Date().getTime());
pm.environment.set("api-time", timeStr);

pm.request.body = body;

java后端签名计算

class ApiPreHandler {
    public static String generateHmac256(String msg, String key) {
        if (Objects.isNull(msg)) {
            return "";
        }
        if (Objects.isNull(key)) {
            return msg;
        }
        final String algorithm = "HmacSHA256";
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm);
            Mac mac = Mac.getInstance(algorithm);
            mac.init(secretKeySpec);
            byte[] bytes = mac.doFinal(msg.getBytes());
            return Base64.getEncoder().encodeToString(bytes);
        } catch (Exception e) {
            LogUtils.error(e.getMessage(), e);
            return "";
        }
    }

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        String method = request.getMethod().toLowerCase(Locale.ROOT);
        String querySorted = getSortedQuery(request.getQueryString());
        String body = request.getReader().lines().collect(Collectors.joining());
        String apiSignature = request.getHeader("api-signature");
        String signStrSrc = String.join("&", Lists.newArrayList(apiKey, method, querySorted, body));
        log.info("requestURI: {}, singStrSrc={}", requestURI, signStrSrc);
        String calcSign = SecurityUtils.generateHmac256(signStrSrc, apiSecret);
        if (!Objects.equals(apiSignature, calcSign)) {
            log.error("api-signature denied, signStrSrc={}", signStrSrc);
            return false;
        }
        return true;
    }
}