一、前言
由于之前写过的一片文章 (java接口签名(Signature)实现方案 )收获了很多好评,此次来说一下另一种简单粗暴的签名方案。相对于之前的签名方案,对body、paramenter、path variable的获取都做了简化的处理。也就是说这种方式针所有数据进行了签名,并不能指定某些数据进行签名。
二、签名规则
1、线下分配appid和appsecret,针对不同的调用方分配不同的appid和appsecret
2、加入timestamp(时间戳),10分钟内数据有效
3、加入流水号nonce(防止重复提交),至少为10位。针对查询接口,流水号只用于日志落地,便于后期日志核查。 针对办理类接口需校验流水号在有效期内的唯一性,以避免重复请求。
4、加入signature,所有数据的签名信息。
以上红色字段放在请求头中。
三、签名的生成
signature 字段生成规则如下。
1、数据部分
Path Variable:按照path中的字典顺序将所有value进行拼接
Parameter:按照key=values(多个value按照字典顺序拼接)字典顺序进行拼接
Body:从request inputstream中获取保存为String形式
如果存在多种数据形式,则按照body、parameter、path variable的顺序进行再拼接,得到所有数据的拼接值。
上述拼接的值记作 Y。
2、请求头部分
X=”appid=xxxnonce=xxxtimestamp=xxx”
3、生成签名
最终拼接值=XY
最后将最终拼接值按照如下方法进行加密得到签名。
signature=org.apache.commons.codec.digest.HmacUtils.AesEncodeUtil(app secret, 拼接的值);
四、签名算法实现
注:省去了X=”appid=xxxnonce=xxxtimestamp=xxx”这部分。
1、自定义Request对象
为什么要自定义request对象,因为我们要获取request inputstream(默认只能获取一次)。
public class BufferedHttpServletRequest extends HttpServletRequestWrapper { private ByteBuf buffer; private final AtomicBoolean isCached = new AtomicBoolean(); public BufferedHttpServletRequest(HttpServletRequest request, int initialCapacity) { super(request); int contentLength = request.getContentLength(); int min = Math.min(initialCapacity, contentLength); if (min < 0) { buffer = Unpooled.buffer(0); } else { buffer = Unpooled.buffer(min, contentLength); } } @Override public ServletInputStream getInputStream() throws IOException { //Only returning data from buffer if it is readonly, which means the underlying stream is EOF or closed. if (isCached.get()) { return new NettyServletInputStream(buffer); } return new ContentCachingInputStream(super.getInputStream()); } public void release() { buffer.release(); } private class ContentCachingInputStream extends S