如何在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
相关产品推荐
相关产品推荐

