HTTP接口签名校验

文章目录

  • 为什么要签名校验
  • 常用签名校验算法
  • 实例

为什么要签名校验

一般对外的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);
    }
}

你可能感兴趣的:(Java后端技术栈,Spring,http,java)