R Shiny: аутентификация пользователя для одного приложения. R

Я занимаюсь разработкой приложения R Shiny и хочу добавить имя пользователя и логины. Я проверил демоверсию RStudio, но она использует только ShinyServer Pro, и я использую пакет mongolite для резервного копирования formData на Mongodb.

Есть ли способ принудительно добавить логины пользователя перед созданием пользовательского интерфейса приложения?

Ответ 1

ShinyProxy, сервер с открытым исходным кодом Docker- и Spring Shiny, основанный на Java, был разработан для решения этой проблемы. Он позволяет жестко кодировать пользователей в файле конфигурации приложения, подключаться к серверу LDAP, использовать SSO/Keycloak или вход в социальную сеть.

Ответ 2

Вот пример того, как использовать куки для аутентификации. Дополнительную информацию можно найти в моем блоге здесь.

Сначала загрузите cookie js в папку www/:

if (!dir.exists('www/')) {
    dir.create('www')
}

download.file(
  url = 'https://cdn.jsdelivr.net/npm/[email protected]/src/js.cookie.min.js',
  destfile = 'www/js.cookies.js'
)

Установите необходимые пакеты:

install.packages(c('shiny', 'shinyjs', 'bcrypt'))

Сохраните следующий код как app.R и нажмите кнопку "Запустить приложение":

library(shiny)
library(shinyjs)
library(bcrypt)


# This would usually come from your user database.

# Never store passwords as clear text
password_hash <- hashpw('secret123') 

# Our not so random sessionid
# sessionid <- paste(
#   collapse = '', 
#   sample(x = c(letters, LETTERS, 0:9), size = 64, replace = TRUE)
# )
sessionid <- "OQGYIrpOvV3KnOpBSPgOhqGxz2dE5A9IpKhP6Dy2kd7xIQhLjwYzskn9mIhRAVHo" 


jsCode <- '
  shinyjs.getcookie = function(params) {
    var cookie = Cookies.get("id");
    if (typeof cookie !== "undefined") {
      Shiny.onInputChange("jscookie", cookie);
    } else {
      var cookie = "";
      Shiny.onInputChange("jscookie", cookie);
    }
  }
  shinyjs.setcookie = function(params) {
    Cookies.set("id", escape(params), { expires: 0.5 });  
    Shiny.onInputChange("jscookie", params);
  }
  shinyjs.rmcookie = function(params) {
    Cookies.remove("id");
    Shiny.onInputChange("jscookie", "");
  }
'

server <- function(input, output) {

  status <- reactiveVal(value = NULL)
  # check if a cookie is present and matching our super random sessionid  
  observe({
    js$getcookie()
    if (!is.null(input$jscookie) && 
        input$jscookie == sessionid) {
          status(paste0('in with sessionid ', input$jscookie))
    }
    else {
      status('out')
    }
  })

  observeEvent(input$login, {
    if (input$username == 'admin' & 
        checkpw(input$password, hash = password_hash)) {
      # generate a sessionid and store it in your database,
      # sessionid <- paste(
      #   collapse = '', 
      #   sample(x = c(letters, LETTERS, 0:9), size = 64, replace = TRUE)
      # )
      # but we keep it simple in this example...
      js$setcookie(sessionid)
    } else {
      status('out, cause you don\'t know the password secret123 for user admin.')
    }
  })

  observeEvent(input$logout, {
    status('out')
    js$rmcookie()
  })

  output$output <- renderText({
    paste0('You are logged ', status())}
  )
}

ui <- fluidPage(
  tags$head(
    tags$script(src = "js.cookies.js")
  ),
  useShinyjs(),
  extendShinyjs(text = jsCode),
  sidebarLayout(
    sidebarPanel(
      textInput('username', 'User', placeholder = 'admin'),
      passwordInput('password', 'Password', placeholder = 'secret123'),
      actionButton('login', 'Login'),
      actionButton('logout', 'Logout')
    ),
    mainPanel(
      verbatimTextOutput('output')
    )
  )
)

shinyApp(ui = ui, server = server)

Ответ 3

Ну, вы можете сделать это через код, используя renderUI и изменяя пользовательский интерфейс на лету. Вот пример того, как это сделать:

library(shiny)
library(ggplot2)

u <- shinyUI(fluidPage(
  titlePanel("Shiny Password"),

  sidebarLayout(position = "left",
                sidebarPanel( h3("sidebar panel"),
                              uiOutput("in.pss"),
                              uiOutput("in.clr"),
                              uiOutput("in.titl"),
                              uiOutput("in.cnt"),
                              uiOutput("in.seed")

                ),
                mainPanel(h3("main panel"),
                          textOutput('echo'),
                          plotOutput('stdplot')
                )
  )
))

pok <- F

s <- shinyServer(function(input, output) 
{
  output$in.pss   <- renderUI({ input$pss; if (pok) return(NULL) else return(textInput("pss","Password:","")) })
  output$in.clr   <- renderUI({ input$pss; if (pok) return(selectInput("clr","Color:",c("red","blue"))) else return(NULL) })
  output$in.titl  <- renderUI({ input$pss; if (pok) return(textInput("titl","Title:","Data")) else return(NULL) })
  output$in.cnt   <- renderUI({ input$pss; if (pok) return(sliderInput("cnt","Count:",100,1000,500,5)) else return(NULL) })
  output$in.seed  <- renderUI({ input$pss; if (pok) return(numericInput("seed","Seed:",1234,1,10000,1)) else return(NULL) })
  histdata <- reactive(
    {
      input$pss;
      validate(need(input$cnt,"Need count"),need(input$seed,"Need seed"))
      set.seed(input$seed)
      df <- data.frame(x=rnorm(input$cnt))
    }
  )
  observe({
     if (!pok) {
       password <- input$pss
       if (!is.null(password) && password == "pass") {
         pok <<- TRUE
       }
     }
   }
  )
  output$echo = renderText(
    {
      if (pok) {
        s <- sprintf("the %s is %s and has %d rows and uses the %d seed",
           input$ent,input$clr,nrow(histdata()),input$seed)
      } else {
        s <- ""
      }
      return(s)
    }
  )
  output$stdplot = renderPlot(
    {
      input$pss
      if (pok) {
        return(qplot(data = histdata(),x,fill = I(input$clr),binwidth = 0.2,main=input$titl))
      } else {
        return(NULL)
      }
    }
  )
}
)
shinyApp(ui=u,server=s)

Урожайность

это при входе в систему:

enter image description here

И это после того, как вы ввели зашифрованный пароль "pass".

enter image description here

Конечно, программирование таким способом немного неудобно, но вы можете использовать вкладки и скрывать их, возможно, используя аналогичную логику.

Или, если вы используете блестящий сервер, вы, вероятно, можете поставить фильтр перед сайтом. Но вот как бы я подошел к этому в Shiny.

Ответ 4

Вы можете добавить аутентификационный прокси перед своим блестящим приложением следующим образом: https://www.datascienceriot.com/add-authentication-to-shiny-server-with-nginx/kris/

Это скелетная конфигурация Nginx, перенаправляющая с порта HTTPS 443 на ваш Блестящий сервер, работающий на порту 8000.

server {
    listen       443;
    server_name  shinyservername;

    ssl                  on;
    ssl_certificate      ...
    ssl_certificate_key  ...
    ssl_dhparam ...

    location / {
        proxy_pass http://yourdestinationIP:8000;
        proxy_set_header        X-Forwarded-Proto $scheme;
        add_header              Front-End-Https   on;
        proxy_set_header        Accept-Encoding   "";
        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/htpasswd;
    }
}

Установите брандмауэр вашего хоста, чтобы открыть порт 443, и разрешить только локальные подключения к Shiny Server на порту 8000:

iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp -s localhost --dport 8000 -j ACCEPT
iptables -A INPUT -p tcp --dport 8000 -j DROP

Добавить статические учетные данные для одного или нескольких пользователей в /etc/nginx/htpasswd:

htpasswd –c /etc/nginx/htpasswd myshinyuser

Один недостаток (из многих) заключается в том, что он будет аутентифицироваться и авторизироваться, но он не будет передавать аутентифицированную информацию пользователя в ваше приложение. Для этого вам понадобится интеграция аутентификации Shiny Server Pro, которая передаст вам пользователя в сеансе.

Ответ 5

Недавно я написал пакет R, который предоставляет модули входа/выхода из системы, которые можно интегрировать с любой структурой пользовательского интерфейса начальной загрузки.

Блогпост с примером использования shinydashboard

Пакет репо

каталог inst/ в репозитории содержит код для примера приложения.

Ответ 6

Я использую shinyAppLogin вместо shinApp:

# R code
shinyAppLogin <- function( ui, server, title="Restricted Area", accounts = list(admin="admin"), ...) {
    ui_with_login <- bootstrapPage(theme = "login_style.css",
        div(class="login-page",
            div(class="form",
                h1(title), br(),
                tags$form(class="login-form",
                    textInput(inputId = "user", label = NULL, placeholder="Username"),
                    passwordInput(inputId = "pass", label = "", placeholder = "Password" ),
                    actionButton(inputId = "login", label = "Login")
            ) ) ) )

    server_with_login <- function(input, output, session) {

        observeEvent(input$login, ignoreNULL = T, {

        if ( input$user %in% names(accounts) && input$pass == accounts[[input$user]] ) {

            removeUI(selector = "body", multiple = T, immediate = T, session = session)
            insertUI(selector = "html", where = "beforeEnd", ui = ui, immediate = T, session = session )
            server(session$input, session$output, session = session)
        }
    } ) }

    shinyApp(ui = ui_with_login, server = server_with_login, ...)
}

тогда мой код будет: shinyAppLogin (my_ui, my_server)

Экран входа в стили

то я использовал css из введите описание ссылки здесь просто сохраните свой css в www/login_style.css