前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >redis实现防御

redis实现防御

作者头像
崔笑颜
发布2020-11-03 14:34:08
6830
发布2020-11-03 14:34:08
举报
文章被收录于专栏:小小码农一个。

由于redis是基于内存的缓存服务器,性能高,稳定性也经住了市场的考验,自定义拦截器,连接请求,使用redis来记录每个http请求ip的近期访问次数,w如果发现了异常情况(短时间内发送了大量请求)则可以认为是恶意的HTTP请求攻击.可以对该IP进行封锁处理.并且可以记录该IP到日志上.

二、程序步骤 1.引入依赖和配置redis 为了方便测试,我使用工作电脑调试和运行springboot项目,而redis服务器搭建在阿里云上.

代码语言:javascript
复制
<!--redis 依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

#redis配置
spring.redis.host = ...
spring.redis.port = ... [默认6379]
spring.redis.password = [如果你的redis服务器有密码]

2.自定义redis的序列化器和RedisTemplate 我们需要一种<String, Integer>类型的RedisTemplate,并且要求他的value能够灵活地在java中作为integer 而在redis中作为string,而不用通过我们手工转换,同时也能保证在redis服务器上的可读性. springboot上的redis整合api中提供了愚蠢的RedisTemplate<String, Integer> 由于Integer的序列化等问题显然不符合我们的要求,只能定制 一个自己的RedisTemplate 和序列化器,我习惯建一个config包,在该包的类中通过bean方法创建自己的配置bean

2.1.序列化器 定义一个类 实现RedisSerializer接口,重写其序列化和反序列化方法

实现思路很简单,因为我们的目的是在redis中以string字符储存, 在java中以integer的形式出现,在序列化时候将integer转换为string再进行序列化,反序列化时将序列化后的string转换成integer即可

代码语言:javascript
复制
@Override
public byte[] serialize(@Nullable Integer integer) throws SerializationException {
    return integer== null ? null : integer.toString().getBytes(StandardCharsets.UTF_8);
}
 @Override
public Integer deserialize(@Nullable byte[] bytes) throws SerializationException {
    return bytes == null ? null : Integer.valueOf(new String(bytes, 
        StandardCharsets.UTF_8));
}

2.2.RedisTemplate 我们可以简单地创建一个RedisTemplate<String, Integer> 对象,对其key采用string序列化策略(注意,不要使用愚蠢的jdk默认序列化器,其序列化策略并不会原原本本地将string对象作为字符地byte数组存储,而且将其序列化为一种远古人类使用地奇怪符号) 对其value采用我们上文自定义的序列化器,所以核心的操作是

setKeySerializer(new StringRedisSerializer());

setValueSerializer(intRedisSerializer);

全部代码如下

@Configuration public class RedisConfig {

代码语言:javascript
复制
@Autowired
RedisSerializer<Integer> intRedisSerializer;

@Bean("intRedisTemplate")
public RedisTemplate<String, Integer> IntRedisTemplate(RedisConnectionFactory rcf){
    RedisTemplate<String, Integer> re = new RedisTemplate();
    re.setConnectionFactory(rcf);
    re.setKeySerializer(new StringRedisSerializer());
    re.setValueSerializer(intRedisSerializer);

    return re;
}

} 说明:我将前面的integer序列化器命名为intRedisSerializer,注入了该RedisTemplate中

3.侦探类实现检测http攻击的核心功能 聪明的你可能已经发现字体的颜色变深了,这并不是因为我不知道怎么改字体颜色,而是我们的核心代码要开始了!!!

侦探(HttpDetective)这个名字花了我近10分钟, 该接口只声明了一个inspection(String ip)方法, 决定这个ip是否能访问你的服务器, 然后我们再创建HttpDetectiveImpl来具体实现他的功能,我们使用了一种简单可容错的策略,忽略了异步可能导致的实际参数误差,但是这并不会影响我们的功能和安全性. 具体代码如下

@Component("httpDetective") public class HttpDetectiveImpl implements HttpDetective {

代码语言:javascript
复制
@Autowired
private RedisTemplate<String, Integer> intRedisTemplate;

/**单位均为毫秒*/
private final int RECORD_TIME = 1000;
private final int ALLOW_TIMES = 6;
private final int REFUSE_TIME = 180000;


@Override
public boolean inspection(String ip) {
    Integer times = intRedisTemplate.opsForValue().get(ip);
    if(times == null){
        System.out.println("有正常人进入");
        intRedisTemplate.opsForValue().set(ip, 1, RECORD_TIME, TimeUnit.MILLISECONDS);
        return true;
    }else{
        if(times >= ALLOW_TIMES){
            System.out.println("认定为入侵行为 拦截访问 并且禁止目标短时间内再次访问并且记录 入侵者ip"+ ip);
            intRedisTemplate.opsForValue().set(ip, ALLOW_TIMES, REFUSE_TIME, TimeUnit.MILLISECONDS);
            return false;
        }else{
            System.out.println("有可疑人进入.  "+times);
            intRedisTemplate.opsForValue().increment(ip);
            return true;
        }
    }
}

}

我们将前面的RedisTemplate注入侦探类作为侦探类的工具,为了方便后面的修改,我们定义了三个常量,单位都是毫秒

代码语言:javascript
复制
RECORD_TIME:  检查http访问的时间间隔
ALLOW_TIMES :  检测时间间隔内的访问的次数
REFUSE_TIME:   对判定为入侵者的封禁时间

我们将客户端传入的ip作为redis的键,当客户端首次访问时候,会创建该键值对,并且设置其生存周期 也就是RECORD_TIME,我们这里设置为一秒,这一秒内 用户每当再次访问接口,对应的value就会自己加一,如果在生存周期内值加到ALLOW_TIMES时,会将其设为入侵者,并且其在REFUSE_TIME时间内无法再访问我们的url.即对该ip封禁的时间内都会返回false,并且若入侵者继续尝试访问http接口时都会刷新封禁时间,这里将其注册为名字为httpDetective的bean

4.拦截器中挂载该侦探类,实现http拦截检测 4.1 自定义拦截器 由于拦截器通过返回true和false来决定是否发行,这里只需要直接注入上面的侦探类返回其inspection方法即可.

@Autowired HttpDetective httpDetective;

@Bean public HandlerInterceptor visitorRegistration(){

代码语言:javascript
复制
return new HandlerInterceptor(){
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return httpDetective.inspection(request.getRemoteAddr());
    }
};

} 4.3 在springboot中注册拦截器 springboot注册拦截器非常方便,只要继承了WebMvcConfigurer并且在其 addInterceptors(InterceptorRegistry registry) 方法中添加改拦截器即可,注意把该类注解为@Configuratio, 这里不必多说

@Configuration public class WebMvcConfig implements WebMvcConfigurer {

代码语言:javascript
复制
@Resource
HandlerInterceptor visitorRegistration;

@Override
public void addInterceptors(InterceptorRegistry registry) {

    System.out.println("添加拦截器");
    // TODO Auto-generated method stub
    registry.addInterceptor(visitorRegistration)
            // 拦截路劲
            .addPathPatterns("/**");

}

} 三、功能测试 我使用已经开发出雏形的二手商城为例子,先开启服务器

image.png
image.png

服务器正常启动

打开浏览器,疯狂刷http(很多人可能远远达不到我的手速,也可以选择通过编写程序刷url) ![image.png](https://cuixiaoyande.oss-cn-shanghai.aliyuncs.com/image_1604197678964.p我们可以看到 在我一秒20刷的http请求中,在第六次之后的请求全部被拦截下来,并且3分钟内无法再访问

服务器中的redis也有对应的redis记录ng)

image.png
image.png
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-11-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档