场景:外部系统需要直接通过http方式调用内部系统的接口获取数据
方案:创建新的controller,将controller的映射地址配置在新建的interceptor(拦截器)中,使得拦截器能拦截到访问该controller的地址,在拦截器中做接口加签的合法性验证:
//获取请求方的各种信息进行后续的验证使用
url = acquireURL(request);
timestamp = request.getHeader("X-Timestamp");
nonce = request.getHeader("X-Nonce");
appKey = request.getHeader("X-Appkey");
signature = request.getHeader("X-Signature");
body = acquireBody(request);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
AuthEntity entity = new AuthEntity(request);
LOGGER.info("验签字符串:" + entity.toString());
if (checkRequired(entity)//验证必填字段非空
&& checkTimestamp(entity.getTimestamp())//request header中传输的时间戳,用于验证发起的接口是否已经过期,和系统当前时间比较后得出,暂时是超过1小时算过期
&& checkNonce(entity.getNonce())//request header中传输的UUID,防止接口重复调用,从redis中获取该值是否为null来判断是否重复提交
&& SignatureUtil.checkSignature(entity)) {//根据头标签所有属性和报文内容再加密钥生成签名判断报文内容是否被篡改
redisTemplate.opsForValue().set(entity.getNonce(), "1", EXPIRE_TIME+5, TimeUnit.SECONDS);//使用redis保存UUID并设置过期时间
return true;
}
response.setStatus(401); // Unauthorized
return false;
}
签名生成方法:
private static String generateSignature(String data, String secret) {
byte[] result = null;
try {
SecretKeySpec signinKey = new SecretKeySpec(secret.getBytes("utf-8"), HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signinKey);
byte[] rawHmac = mac.doFinal(data.getBytes("utf-8"));
result = Base64.encodeBase64(rawHmac);
return new String(result,"utf-8");
} catch (InvalidKeyException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
LOGGER.error(e.toString());
}
return null;
}