fastapi限制ip访问 中间件+redis封装

1,021 阅读2分钟

1.前言

这段时间学院要求参加去做项目并拿着做好的项目去参加比赛,我是打算做一套验证码的产品,因为考虑到一款验证码产品需要考虑到高并发的处理能力,又因为在所有Web框架里我只对python web稍微了解一点,经过权衡决定用fastapi这个框架来做,昨天晚上睡觉的时候突然想到一款验证码产品对于ip访问的限制和追踪那肯定是必不可少的,想直接在网上找现成的也没找到,于是就决定自己手搓

2.代码实现

话不多说直接上代码

from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import JSONResponse
from fastapi import Request, status


# 限制访问ip的中间件
class RateLimiterMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):

        #redis.Redis的实例,用于连接数据库
        r2 = request.app.state.r2

        #获取客户端的ip
        client_ip = request.client.host

        key = f"ip:{client_ip}"
        
        ban_key = f"ban:{client_ip}"
        
        limit = 100  # 限制次数
        
        period = 300  # 时间窗口(秒)
        
        ban_period = 300  # 封禁时间(秒)

        # 检查是否在黑名单中
        if await r2.get(ban_key):
            resp={'msg':'Too Many Requests. You are banned for 5 minutes.'}

            return JSONResponse(
                content=resp, status_code=status.HTTP_403_FORBIDDEN
            )

        # 检查 Redis 中的访问次数
        visit_count = await r2.get(key)
        if visit_count is None:
            # 如果没有记录,初始化访问次数
            await r2.setex(key, period, 1)
        else:
            # 如果有记录,增加访问次数
            visit_count = int(visit_count)
            if visit_count >= limit:
                # 如果访问次数超过限制,加入黑名单并返回 429 Too Many Requests
                await r2.setex(ban_key, ban_period, 1)
                # 拉入黑名单后删除记录访问次数的信息
                await r2.delete(key)
                resp = {'msg': 'Too Many Requests. You are banned for 5 minutes.'}

                return JSONResponse(
                    content=resp, status_code=status.HTTP_403_FORBIDDEN
                )
            else:
                # 否则,增加访问次数
                await r2.incr(key)

        # 继续处理请求
        response = await call_next(request)
        return response

在这个中间件中,需要导入你自己的redis实例,并且将时间窗口,限制次数,以及封禁时间给修改成你自己想要的。以我的代码举例,就是300秒内限制访问100次,若超出,则封禁300秒才能访问。中间件的作用效果是全局的,若你只想对特定的接口进行限制,可以使用装饰器

3.结语

因为本人不是软院出身的,对于开发的见解还是非常浅薄的,希望大家能多多指正