0Pricing
R Academy · 课时

身份验证与 API 安全

添加 API 密钥验证、CORS 标头和速率限制过滤器

身份验证与 API 安全 是 CoddyKit 上的免费 R Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 R Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 R Academy 课程共包含 4 节课。

API 安全为何重要

Plumber API 是一个公开的 HTTP 服务器。如果没有身份验证,任何能够访问该端口的人都可以调用您的端点。安全层包括身份验证(您是谁?)、授权(您能做什么?)、输入验证和传输安全。

将 Plumber 过滤器用作中间件

Plumber 中的过滤器会在路由处理函数之前运行。使用 pr_filter(name, function(req, res){...}) 添加中间件,以检查每个请求。调用 plumber::forward() 将请求传递给下一个过滤器或路由;如需拒绝请求,则提前返回。

# library(plumber)
# pr <- plumb('api.R')
# pr |>
#   pr_filter('logger', function(req, res) {
#     cat(req$REQUEST_METHOD, req$PATH_INFO, '
')
#     plumber::forward()  # must call to continue
#   }) |>
#   pr_run(port = 8000)

API 密钥身份验证过滤器

服务器到服务器 API 最常见的简单身份验证模式,是在请求标头中传递静态 API 密钥。过滤器会检查每个请求的标头,如果缺少密钥或密钥错误,则返回 401。

# In api.R:
# VALID_KEY <- Sys.getenv('API_SECRET_KEY')
#
# #* @filter auth
# function(req, res) {
#   key <- req$HTTP_X_API_KEY
#   if (is.null(key) || key != VALID_KEY) {
#     res$status <- 401L
#     return(list(error = 'Unauthorized'))
#   }
#   plumber::forward()
# }

检查 Authorization 标头

Bearer 令牌会通过 Authorization: Bearer <token> 标头传递。您可以通过 req$HTTP_AUTHORIZATION 访问它。使用 strsplit() 解析标头以提取令牌部分,然后根据您的存储进行验证。

# #* @filter bearer_auth
# function(req, res) {
#   auth_header <- req$HTTP_AUTHORIZATION
#   if (is.null(auth_header) || !startsWith(auth_header, 'Bearer ')) {
#     res$status <- 401L
#     return(list(error = 'Bearer token required'))
#   }
#   token <- substring(auth_header, 8)  # strip 'Bearer '
#   if (!token_is_valid(token)) {
#     res$status <- 401L
#     return(list(error = 'Invalid token'))
#   }
#   plumber::forward()
# }

使用 #* @preempt 跳过身份验证

某些端点(例如运行状况检查和公开文档)应跳过身份验证。请使用 #* @preempt auth 为这些端点添加注解,其中 auth 与过滤器名称相匹配。Plumber 会将请求直接路由到处理函数,绕过该过滤器。

# #* Health check — no auth required
# #* @preempt auth
# #* @get /ping
# function() {
#   list(status = 'ok', time = as.character(Sys.time()))
# }
#
# #* Protected endpoint — goes through auth filter
# #* @get /data
# function() {
#   list(secret = 'sensitive data')
# }

使用 pr_cors() 配置 CORS

如果您的 API 会被其他域中的浏览器调用,就必须启用 CORS(跨源资源共享)。使用 pr_cors() 配置允许的源、方法和标头,无需手动编写原始标头。

# library(plumber)
# pr <- plumb('api.R')
# pr |>
#   pr_cors(
#     origin            = 'https://myapp.example.com',
#     methods           = c('GET', 'POST'),
#     headers           = c('Content-Type', 'X-API-Key'),
#     credentials       = TRUE
#   ) |>
#   pr_run(port = 8000)

输入清理——绝不要信任用户输入

在查询或文件操作中使用输入之前,始终对其进行验证和清理:

  • 检查类型:is.numeric()、is.character()
  • 检查范围:id >= 1 && id <= 1e9
  • 拒绝意外字符:grepl('[^a-zA-Z0-9_]', name)
  • 绝不要将用户字符串直接插入 SQL——请使用参数化查询
# #* @post /search
# function(req, res) {
#   body <- jsonlite::fromJSON(req$postBody)
#   query <- body$query
#   if (!is.character(query) || nchar(query) > 200) {
#     res$status <- 400L
#     return(list(error = 'query must be a string <= 200 chars'))
#   }
#   if (grepl('[;\'"]', query)) {
#     res$status <- 400L
#     return(list(error = 'Invalid characters in query'))
#   }
#   list(results = search_db(query))
# }

速率限制概念

Plumber 没有内置的速率限制器,但您可以在过滤器中使用共享环境,按 IP 跟踪请求数量,从而自行实现速率限制:

  • 在 R 环境中按 IP 记录每个请求的时间戳
  • 如果请求数量超过时间窗口内的限制,则返回 429 并拒绝请求
  • 在生产环境中,请使用 nginx 等反向代理来执行速率限制
# request_log <- new.env()
#
# #* @filter rate_limit
# function(req, res) {
#   ip <- req$REMOTE_ADDR
#   now <- as.numeric(Sys.time())
#   if (!exists(ip, envir = request_log)) assign(ip, c(), envir = request_log)
#   times <- get(ip, envir = request_log)
#   times <- times[times > now - 60]   # last 60 seconds
#   if (length(times) >= 60) { res$status <- 429L; return(list(error='Too Many Requests')) }
#   assign(ip, c(times, now), envir = request_log)
#   plumber::forward()
# }

安全存储 API 密钥

绝不要将机密信息硬编码在源文件中。请将其存储在环境变量中,并在启动时使用 Sys.getenv() 读取。在本地使用 .env 文件(将其从 git 中排除),在生产环境中则通过部署环境注入机密信息。

# In .env (never commit this file):
# API_SECRET_KEY=my_super_secret_key_here
#
# In api.R:
# VALID_KEY <- Sys.getenv('API_SECRET_KEY', unset = '')
# if (nchar(VALID_KEY) == 0) stop('API_SECRET_KEY not set')
#
# Load .env locally (devtools::load_dot_env or Sys.setenv):
# readRenviron('.env')
cat('Sys.getenv reads API keys without exposing them in source
')

将用户上下文附加到请求

在身份验证过滤器中验证令牌后,请将解码后的用户信息附加到 req 对象,这样下游处理函数无需重新验证即可访问这些信息。req 上的自定义字段会在整个过滤器链中保留。

# #* @filter auth
# function(req, res) {
#   token <- req$HTTP_AUTHORIZATION
#   user <- validate_token(token)  # returns list(id=1, role='admin')
#   if (is.null(user)) { res$status <- 401L; return(list(error='Unauthorized')) }
#   req$user <- user   # attach to request
#   plumber::forward()
# }
#
# #* @get /profile
# function(req) {
#   list(user_id = req$user$id, role = req$user$role)
# }

使用 tryCatch 处理错误

将端点逻辑包装在 tryCatch() 中,以捕获意外错误,并返回整洁的 500 响应,而不是导致工作进程崩溃或向调用者泄露堆栈跟踪。

# #* @get /risky/<id:int>
# function(id, res) {
#   tryCatch({
#     result <- risky_db_call(id)
#     list(data = result)
#   }, error = function(e) {
#     message('Error in /risky: ', conditionMessage(e))
#     res$status <- 500L
#     list(error = 'Internal server error')
#   })
# }

快速检查:@preempt 注解

#* @preempt auth 注解会对 Plumber 端点执行什么操作?

API 安全回顾

保护 Plumber API 需要采用分层防御:

  • pr_filter('auth', ...)——在中间件中检查每个请求
  • req$HTTP_AUTHORIZATION / req$HTTP_X_API_KEY——读取身份验证标头
  • #* @preempt auth——为公开端点跳过身份验证
  • pr_cors()——配置浏览器跨源访问
  • 在任何数据库或文件操作之前验证输入
  • 使用 Sys.getenv() 获取机密信息——绝不要将密钥硬编码

常见问题解答

「身份验证与 API 安全」课时是免费的吗?

是的 — 「身份验证与 API 安全」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 R Academy 课程的其余内容,请升级到 CoddyKit PRO。 R Academy 课程共包含 4 节课。

「身份验证与 API 安全」这节课中我会学到什么?

添加 API 密钥验证、CORS 标头和速率限制过滤器 你通过在浏览器中直接运行的动手代码来练习 R Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 R Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 R Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「身份验证与 API 安全」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 R Academy 课中编写并运行代码吗?

能。每节 R Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Plumber 与 REST 入门
  2. 创建 GET 和 POST 端点
  3. 身份验证与 API 安全
  4. 将 Plumber API 部署到生产环境
← 返回 R Academy