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

如何通过自定义HTML登录页访问Shiny应用

自定义登录页访问Shiny APP的配置方案

你可以通过纯Shiny+JavaScript或结合PHP后端验证两种方式实现自定义登录页访问Shiny APP,以下是具体步骤:


方案一:纯Shiny+JavaScript前端验证

这种方案无需额外后端服务,直接在Shiny内完成登录验证,适合简单场景。

1. 修改Shiny代码

调整服务器逻辑,维护登录状态,动态切换登录页与主UI:

library(shiny)

ui <- fluidPage(
  uiOutput("main_content")
)

server <- function(input, output, session) {
  # 登录状态标记
  is_logged_in <- reactiveVal(FALSE)
  # 合法用户列表(实际项目建议替换为数据库查询)
  valid_users <- list(
    "user1@example.com" = "password123",
    "user2@example.com" = "456password"
  )

  # 动态渲染内容:未登录显示登录页,已登录显示原Shiny界面
  output$main_content <- renderUI({
    if (!is_logged_in()) {
      htmlTemplate("www/index.html")
    } else {
      fluidPage(
        sidebarLayout(
          sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
          ),
          mainPanel(
            plotOutput("distPlot")
          )
        )
      )
    }
  })

  # 处理登录请求
  observeEvent(input$login_attempt, {
    username <- input$login_username
    password <- input$login_password

    if (!is.null(username) && !is.null(password) && 
        exists(username, where = valid_users) && 
        valid_users[[username]] == password) {
      is_logged_in(TRUE)
    } else {
      showModal(modalDialog(
        title = "登录失败",
        "用户名或密码错误,请重试",
        easyClose = TRUE
      ))
    }
  })

  # 原有绘图逻辑
  output$distPlot <- renderPlot({
    x    <- faithful[, 2]
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    hist(x, breaks = bins, col = 'darkgray', border = 'white')
  })
}

shinyApp(ui = ui, server = server)

2. 修改自定义登录页(index.html)

添加JavaScript监听表单提交,将用户名密码发送给Shiny服务器:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>用户登录</title>
    <link href="assets/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="signin.css" rel="stylesheet">
    <!-- 加载Shiny通信所需JS -->
    {{ headContent() }}
  </head>
  <body class="text-center">
<main class="form-signin w-100 m-auto">
  <form id="loginForm">
    <img class="mb-4" src="assets/brand/bootstrap-logo.svg" alt="" width="72" height="57">
    <h1 class="h3 mb-3 fw-normal">请输入用户名和密码</h1>

    <div class="form-floating">
      <input type="email" class="form-control" id="floatingInput" placeholder="name@example.com">
      <label for="floatingInput">邮箱</label>
    </div>
    <div class="form-floating">
      <input type="password" class="form-control" id="floatingPassword" placeholder="密码">
      <label for="floatingPassword">密码</label>
    </div>

    <div class="checkbox mb-3">
      <label>
        <input type="checkbox" value="remember-me"> 记住我
      </label>
    </div>
    <button class="w-100 btn btn-lg btn-primary" type="submit">登录</button>
    <p class="mt-5 mb-3 text-muted">&copy; 2017–2022</p>
  </form>
</main>

<script>
// 监听表单提交,阻止默认跳转
document.getElementById('loginForm').addEventListener('submit', function(e) {
  e.preventDefault();
  // 获取输入值
  const username = document.getElementById('floatingInput').value;
  const password = document.getElementById('floatingPassword').value;
  // 将数据发送给Shiny服务器
  Shiny.setInputValue('login_username', username);
  Shiny.setInputValue('login_password', password);
  Shiny.setInputValue('login_attempt', Math.random()); // 触发登录验证逻辑
});
</script>

方案二:结合PHP后端验证

这种方案将登录验证逻辑放在PHP后端,安全性更高,适合生产环境。

1. 编写PHP登录验证脚本(login.php)

放在独立的Web服务器目录(如Apache/Nginx下),负责验证用户并创建会话:

<?php
session_start();

// 合法用户(实际项目建议从数据库读取加密后的密码)
$valid_users = [
    'user1@example.com' => password_hash('password123', PASSWORD_DEFAULT),
    'user2@example.com' => password_hash('456password', PASSWORD_DEFAULT)
];

$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';

if (isset($valid_users[$username]) && password_verify($password, $valid_users[$username])) {
    $_SESSION['logged_in'] = true;
    $_SESSION['username'] = $username;
    echo json_encode(['success' => true]);
} else {
    echo json_encode(['success' => false, 'message' => '用户名或密码错误']);
}
?>

2. 编写PHP会话验证脚本(check_session.php)

用于Shiny验证用户是否已登录:

<?php
session_id($_POST['session_id']);
session_start();

if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true) {
    echo json_encode(['success' => true]);
} else {
    echo json_encode(['success' => false]);
}
?>

3. 修改登录页(index.html)的JS逻辑

将登录请求发送到PHP脚本:

<!-- 其他部分同前,替换script内容 -->
<script>
document.getElementById('loginForm').addEventListener('submit', function(e) {
  e.preventDefault();
  const username = document.getElementById('floatingInput').value;
  const password = document.getElementById('floatingPassword').value;

  // 发送请求到PHP登录脚本
  fetch('http://你的域名/login.php', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`
  })
  .then(response => response.json())
  .then(data => {
    if (data.success) {
      window.location.href = 'http://你的ShinyAPP地址';
    } else {
      alert(data.message);
    }
  })
  .catch(error => {
    console.error('登录请求失败:', error);
    alert('登录请求失败,请稍后重试');
  });
});
</script>

4. 修改Shiny代码验证会话

使用shinyjs读取PHP会话Cookie,验证用户登录状态:

library(shiny)
library(shinyjs)
library(httr)

ui <- fluidPage(
  useShinyjs(),
  uiOutput("main_content")
)

server <- function(input, output, session) {
  is_logged_in <- reactiveVal(FALSE)

  # 检查PHP会话有效性
  observe({
    session_id <- runjs("return document.cookie.split('; ').find(row => row.startsWith('PHPSESSID='))?.split('=')[1];")
    if (!is.null(session_id)) {
      response <- POST("http://你的域名/check_session.php",
                      body = list(session_id = session_id))
      result <- content(response, "parsed")
      is_logged_in(result$success)
    } else {
      is_logged_in(FALSE)
    }
  })

  # 动态渲染内容
  output$main_content <- renderUI({
    if (!is_logged_in()) {
      htmlTemplate("www/index.html")
    } else {
      fluidPage(
        sidebarLayout(
          sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
          ),
          mainPanel(
            plotOutput("distPlot")
          )
        )
      )
    }
  })

  # 原有绘图逻辑
  output$distPlot <- renderPlot({
    x    <- faithful[, 2]
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    hist(x, breaks = bins, col = 'darkgray', border = 'white')
  })
}

shinyApp(ui = ui, server = server)

注意事项

  1. 安全性:生产环境中禁止明文存储密码,建议使用bcrypt等加密算法存储哈希值。
  2. 跨域问题:Shiny APP与PHP脚本需部署在同一域名下,或配置CORS允许跨域请求。
  3. 会话管理:PHP方案需确保会话Cookie的安全性(设置HttpOnly、Secure属性)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 09:06:19