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

内部共享Shiny应用的用户访问追踪与IP权限管控问询

好问题!针对你的两个需求,我来一步步给你解答:

一、追踪访问Shiny应用的用户

Shiny的session对象里包含了客户端的请求信息,我们可以利用它来获取访问者的IP地址,并记录访问日志。

1. 获取客户端IP并记录日志

在你的server函数中,添加一个observe事件,当用户建立连接时自动记录IP和访问时间:

server <- function(input, output, session) {
  # 追踪用户访问信息
  observe({
    # 确保能获取到IP地址
    req(session$request$REMOTE_ADDR)
    
    user_ip <- session$request$REMOTE_ADDR
    access_time <- format(Sys.time(), "%Y-%m-%d %H:%M:%S")
    
    # 将日志追加到文件中
    log_entry <- sprintf("IP: %s, 访问时间: %s\n", user_ip, access_time)
    cat(log_entry, file = "shiny_access_log.txt", append = TRUE)
  }, once = TRUE) # 确保每个session只执行一次

  # 你的原有server逻辑代码...
}

2. 获取更多用户信息(可选)

如果需要更详细的信息,比如用户的浏览器类型,可以读取session$request$HTTP_USER_AGENT:

user_agent <- session$request$HTTP_USER_AGENT
log_entry <- sprintf("IP: %s, 浏览器: %s, 访问时间: %s\n", user_ip, user_agent, access_time)

注意事项

如果你的Shiny应用部署在反向代理(如Nginx)后面,session$request$REMOTE_ADDR可能会返回代理服务器的IP。这时需要配置代理转发真实客户端IP,并通过session$request$HTTP_X_FORWARDED_FOR来获取:

user_ip <- session$request$HTTP_X_FORWARDED_FOR %||% session$request$REMOTE_ADDR

(注:%||%是rlang包中的函数,用于处理空值,你也可以自己定义:%||% <- function(a, b) if (is.null(a)) b else a)

二、用信号量限制特定IP的访问权限

这里分两种场景:限制特定IP的并发连接数,或者只允许指定IP访问(白名单),我都给你提供实现方案:

1. IP白名单:只允许指定IP访问

如果想让特定IP才能访问应用,可以在用户建立连接时检查IP是否在白名单内,不在则直接关闭session:

# 定义允许访问的IP列表
allowed_ips <- c("192.168.1.100", "192.168.1.101", "10.0.0.5")

server <- function(input, output, session) {
  observe({
    req(session$request$REMOTE_ADDR)
    user_ip <- session$request$REMOTE_ADDR
    
    if (!user_ip %in% allowed_ips) {
      # 提示访问被拒绝
      showModal(modalDialog(
        title = "访问受限",
        "您的IP地址没有权限访问该应用",
        easyClose = FALSE
      ))
      # 关闭当前session
      session$close()
    }
  }, once = TRUE)

  # 你的原有server逻辑代码...
}

2. 信号量限制同一IP的并发连接数

如果想限制同一个IP同时连接应用的数量(比如最多2个并发),可以用全局环境存储IP的连接计数,并配合锁来保证线程安全:

# 全局环境存储每个IP的连接数
ip_connection_counts <- new.env()
# 创建锁,避免多session同时修改计数导致的冲突
ip_lock <- lock()

# 自定义空值处理函数
`%||%` <- function(a, b) if (is.null(a)) b else a

server <- function(input, output, session) {
  user_ip <- session$request$REMOTE_ADDR %||% "unknown"

  # 连接建立时检查并发数
  observe({
    lock(ip_lock)
    current_count <- ip_connection_counts[[user_ip]] %||% 0
    
    if (current_count >= 2) { # 限制每个IP最多2个并发连接
      showModal(modalDialog(
        title = "访问受限",
        "当前IP的并发连接数已达上限,请稍后再试",
        easyClose = FALSE
      ))
      session$close()
    } else {
      # 增加计数
      ip_connection_counts[[user_ip]] <- current_count + 1
    }
    unlock(ip_lock)
  }, once = TRUE)

  # 连接关闭时释放计数
  onSessionEnded(function() {
    lock(ip_lock)
    current_count <- ip_connection_counts[[user_ip]] %||% 1
    
    if (current_count > 1) {
      ip_connection_counts[[user_ip]] <- current_count - 1
    } else {
      # 计数为0时移除该IP记录
      rm(list = user_ip, envir = ip_connection_counts)
    }
    unlock(ip_lock)
  })

  # 你的原有server逻辑代码...
}

注意事项

  • 如果你的Shiny应用是多进程部署(比如用shiny-server的worker配置),全局环境的计数不会在进程间共享,这时需要用外部存储(比如Redis、数据库)来维护连接计数。
  • 同样要注意反向代理场景下的真实IP获取问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:17:00