用Redis的zset防御Session Flood

zset (Sorted Set)是set的升级版本, 在set的基础上增加了一个顺序(或者权重)值属性, 属性在添加修改元素时候可以指定. 每次指定后zset会自动重新按新的值调整顺序. 可以理解为有两列字段的数据表, 一列存value, 一列存顺序编号.

在Redis中, 就会保存一系列的列表, 每个列表是某个IP最近的新建session的记录. 服务端在每次创建session时, 根据ip将此时的(session id, timestamp)记录加入一个zset, 用removeRangeByScore清理时间跨度之外的数据, 检查数量是否超出限制, 最后将整个zset的过期时间更新一下.

对于新建session数量超出的IP, 可以停止新建session, 或将其放入全局黑名单.

    @Override
    public int insert(SessionDTO dto) {
        // ANTI FLD
        RedisZset keyIp = redisFactory.getZset(ID + ":ip:" + dto.getIp());
        keyIp.removeRangeByScore("-inf", String.valueOf(System.currentTimeMillis() - RATE_TIME_FRAME));
        if (keyIp.size() >= RATE_THRESHOLD) {
            logger.error("ANTI FLD: {}", dto.getIp());
            return 0;
        }
        keyIp.add(dto.getId(), System.currentTimeMillis());
        keyIp.exipre(RATE_TIME_FRAME);
        // ANTI FLD END
        redisFactory.pipeline(
                (Pipeline pipeline) -> {
                    pipeline.hset((ID + ":db").getBytes(), dto.getId().getBytes(), SerializeUtil.serialize(dto));
                    pipeline.zadd(ID + ":" + ORDERBY[0], dto.getUpdatedAt(), dto.getId());
                    pipeline.zadd(ID + ":" + ORDERBY[1], dto.getStartedAt(), dto.getId());
                    long expiredAt = dto.getUpdatedAt() + ((dto.getAutologin() == 0) ? ((dto.getUserId().equals(UserDTO.ANONYMOUS_UID)) ? 0 : TTL_LOGIN) : TTL_AUTOLOGIN);
                    pipeline.zadd(ID + ":" + ORDERBY[2], expiredAt, dto.getId());
                    pipeline.zadd(ID + ":" + ORDERBY[3], SerializeUtil.score(dto.getUserId()), dto.getId());
                });
        return 1;
    }

 

posted on 2018-08-07 12:40  Milton  阅读(408)  评论(0编辑  收藏  举报

导航