You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在KTor服务器中封禁客户端IP?含IP获取与校验位置问题

KTor服务器防暴力攻击与IP限流实现方案

一、获取客户端真实IP

你的服务可能面对Chrome、Postman、Curl等各类客户端,甚至被Nginx这类反向代理前置,直接用call.request.remoteHost可能拿到的是代理服务器IP而非真实客户端IP。正确的处理方式是优先读取代理传递的真实IP请求头,再降级到默认的远程主机地址:

fun getClientIp(call: ApplicationCall): String {
    // 优先读取反向代理传递的真实IP头(根据你的代理配置调整,常见的有X-Forwarded-For、X-Real-IP)
    val forwardedFor = call.request.headers["X-Forwarded-For"]
    if (!forwardedFor.isNullOrEmpty()) {
        // X-Forwarded-For可能包含多个IP(格式:客户端IP, 代理1IP, 代理2IP...),取第一个最原始的IP
        return forwardedFor.split(",").first().trim()
    }
    val realIp = call.request.headers["X-Real-IP"]
    if (!realIp.isNullOrEmpty()) {
        return realIp.trim()
    }
    // 无代理场景下,直接获取远程主机IP
    return call.request.remoteHost
}

⚠️ 注意:如果你的服务器前置了反向代理,必须配置代理仅允许信任的IP发送这些头,防止恶意客户端伪造X-Forwarded-For绕过限制。

二、IP校验代码的最优放置位置

要最小化对正常请求的影响,必须在请求进入业务处理前的最早阶段拦截——也就是将校验逻辑注册到ApplicationCallPipeline.Plugins阶段的全局拦截器中。这个阶段是请求到达服务器后的首个处理环节,能在消耗资源处理业务逻辑前就拦截恶意请求。

具体实现示例:

import io.ktor.server.application.*
import io.ktor.http.HttpStatusCode
import java.util.concurrent.ConcurrentHashMap
import kotlin.time.Duration.Companion.minutes
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

// 存储IP请求次数:key为客户端IP,value为(请求计数, 最后请求时间戳)
private val ipRequestCounts = ConcurrentHashMap<String, Pair<Int, Long>>()
// 存储被封禁的IP:key为客户端IP,value为封禁到期时间戳
private val bannedIps = ConcurrentHashMap<String, Long>()
// 互斥锁保证计数更新的线程安全
private val mutex = Mutex()

fun Application.configureRateLimiting() {
    intercept(ApplicationCallPipeline.Plugins) {
        val clientIp = getClientIp(call)
        val currentTime = System.currentTimeMillis()

        // 先检查是否在封禁列表中
        bannedIps[clientIp]?.let { expireTime ->
            if (currentTime < expireTime) {
                call.response.status(HttpStatusCode.TooManyRequests)
                finish() // 直接终止请求,不进入后续处理
                return@intercept
            } else {
                // 封禁到期,移除记录
                bannedIps.remove(clientIp)
            }
        }

        // 统计并校验请求频率
        mutex.withLock {
            val (count, lastRequestTime) = ipRequestCounts.getOrDefault(clientIp, Pair(0, currentTime))
            // 距离上次请求超过1分钟,重置计数
            val newCount = if (currentTime - lastRequestTime > 1.minutes.inWholeMilliseconds) {
                1
            } else {
                count + 1
            }
            ipRequestCounts[clientIp] = Pair(newCount, currentTime)

            // 达到阈值(1分钟100次),封禁IP(示例封禁5分钟)
            if (newCount >= 100) {
                bannedIps[clientIp] = currentTime + 5.minutes.inWholeMilliseconds
                call.response.status(HttpStatusCode.TooManyRequests)
                finish()
                return@intercept
            }
        }

        // 正常请求,继续执行后续业务逻辑
        proceed()
    }
}

这个拦截器会在所有路由处理前执行,一旦检测到恶意请求直接返回429状态码,不会触发后续的业务处理,最大程度减少服务器资源消耗。

如果需要更高级的限流逻辑,也可以结合KTor官方的RateLimit插件,但上述自定义实现更贴合你的IP封禁需求。

内容的提问来源于stack exchange,提问作者Cool_Coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 11:12:49