为什么要签名校验
一般对外的http接口加签的目的是防止数据被篡改。
举个例子,A正在某银行网站给B转账,转入卡号和金额输入完成后生成请求报文,然后加密报文传送给银行后台。银行收到请求后,解密得到明文,然后解析得到B的卡号和转账金额等信息,继续走后续转账流程。
如果传输使用对称加密算法(最常用的),客户端和服务端都是用同一个对称密钥,那么这个对称密钥就存在泄露的可能性。一旦泄露,攻击者X可以截获正常的报文,解密后替换卡号和金额,然后重新用同一个密钥加密被篡改的报文,发送给银行。银行解密后得到的是攻击者X的卡号,然后A的钱就到了X的账户了。
常用签名校验算法
https://cloud.tencent.com/developer/article/1525561
实例
@Slf4j
public class SignCheckInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 之前有个前置filter,将request进行了包装
if(request instanceof RequestWrapper) {
Map<String, String[]> map=request.getParameterMap();
try {
Map<String, String> requestParams = new HashMap<>();
map.keySet().forEach(key->{
requestParams.put(key, StringUtils.join(map.get(key)));
});
if(checkTime(requestParams, response)
&& checkSign(requestParams, response)){
return true;
}
} catch (Exception ex) {
log.error("签名异常", ex);
}
}
responseUtil.noAuthResponse(response, ResponeCode.SIGN_ILLEGAL,ResponeCode.SIGN_ILLEGAL);
return false;
}
private boolean checkTime(Map<String,String> requestParams, HttpServletResponse response) throws Exception{
Long time=Long.parseLong(requestParams.get("time"));
if(System.currentTimeMillis()-time>1000*60*60*24){
log.error("签名过期");
responseUtil.noAuthResponse(response, ResponeCode.SIGN_ILLEGAL.getStarlingKey(),ResponeCode.SIGN_ILLEGAL.getCode());
return false;
}
return true;
}
private boolean checkSign(Map<String,String> map, HttpServletResponse response) throws Exception{
AppRegister app = database.load(map.getAppID());
map.put("systemSecret",app.getSystemSecret());
List<String> keyList=new ArrayList(map.keySet());
// 前端用公钥加密得到的sign
String sign=map.get("sign");
// 用前端传来的map中的所有k、v合成字符串,这个字符串就是生成sign的原材料
Collections.sort(keyList);
keyList.remove("sign");
StringBuffer sb=new StringBuffer();
keyList.forEach(item->{
Object value=map.get(item);
if(value!=null){
sb.append(item);
sb.append(value);
}
});
// 取私钥并在后端生成签名
String buildSign=buildSign();
// 进行签名校验
return buildSign.equals(sign);
}
}