java通过sign签名+时间戳的方式防止rest接口被恶意抓包调用和重放

做后端开发,避免不了要写接口,最开始我们写接口是为了满足实现具体的功能,能在客户端正常调用就行了,这种接口称为裸接口,就跟一个人没有穿衣服一样,我们在家的时候当然可以这么做,肆意放荡,这没关系,但是人总是要出门的,接触一些外人,这时候赤身裸体就不太好了吧。所有这时候我们要给自己穿上一件衣服。我们的接口也是一样的,一旦我们的接口上线暴露给外界了,必须要做一定的防护措施,给接口穿上“一件衣服”,避免一些“偷窥狂”的光临。

要避免接口被恶意调用,只要解决请求重放的问题就行了。

那具体应该要怎么做呢?下面我提供3个方法,供大家参考。

方法1:请求接口的参数中添加时间戳

具体实现原理:每次客户端发送请求时,获取当前时间戳,当参数一同发送到服务器,服务器拦截该请求,获取服务器上当前时间戳和客户端请求中的时间戳进行比较,如果两个时间戳相差超过60s的话就认定为非法请求,因为一般一次正常的http请求发送到服务器不会超过60秒,所以可以这样做。

这样可以做到简单的防重放,但是并不能 真的解决重放的问题,时间戳如果被修改为当前时间戳的话。那这种方式就失效了。

这时候就需要另外一种方式了

  

方法2:请求接口的参数中添加sign签名

具体实现原理:使用签名验证,保证每次请求时候签名只能使用一次。

客户端和服务器端约定一个生成签名串的算法,可以由客户端ip+加密密钥+当前时间戳进行MD5加密生成,前端生成签名串后,跟随参数一起发送到后台解密,并将该签名串保存在redis中,下次请求的时候,先在redis中取查找是否有该签名串,如果有,则说明是非正常请求,这时候就可以认定为非法操作。

但是这样也会存在一个问题,也就是随着时间的推移,redis中会保存越来越多的这样的sign签名,对于服务器资源消耗是一笔不小的开销,这时候需要是我们去清理了,当然我们可以选择给每个sign设定一个过期时间,让他自动清理,比如说设置1天的有效时长,这样当天就不能使用这个sign来请求了,但是一天后还是可以继续使用。这样也有隐患。这时候可以结合时间戳验证和sign签名验证。

方法3:请求接口的参数中添加sign签名+时间戳(推荐

具体实现原理:客户端生成MD5加密签名串,和当前时间戳跟随请求的参数一起发送到后台,后台获取签名进行解密,然后保存到redis中并设置一个失效时长(60s),则60s内不能使用相同的sign发送请求,然后获取系统当前时间戳和前端发送过来的时间戳做比较,如果两者相差超过60s,则认定为非法操作。

这种方式既能保证防止请求重放,又能有效节省服务器资源,推荐使用。

然后就是这样了,不怎么会写文章,写的不好,如果大家哪里有看不懂的话,欢迎加qq群:798733709进行讨论(加群时注明来意和来源),谢谢!

代码参考:

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {


            //接口防刷机制:
            String sign = request.getHeader("sign");
            String timestampStr = (request.getHeader("timestamp"));
            if(sign != null && timestampStr != null){
                //解析签名:时间戳+tbb52sm 双重MD5加密
                long timestamp = Long.parseLong(timestampStr);
                String sign2 = DigestUtils.md5Hex(timestampStr + "tbb52sm");
                sign2 = DigestUtils.md5Hex(sign2);
                //校验签名是否正确?
                if(!sign.equals(sign2)){

                    throw new SelfException(-1, "非法操作");
                }

                //签名存放在redis中,设定1分钟的有效期

                long incr = redisClient.incr("sign:" + sign, 1);
                if(incr == 1){
                    redisClient.expire("sign:" + sign, 60);
                } else {
                    throw new SelfException(-1, "非法操作");
                }

                //时间戳相差超过1分钟,认定为非法操作
                long timestamp2 = System.currentTimeMillis();

                if(Math.abs(timestamp2 - timestamp) > 60 * 1000){

                    throw new SelfException(-1, "非法操作");
                }
            } 
        }
        return true;
    }

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ivan_梦方舟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值