Iniciar la aplicación Shiny después de ingresar la contraseña

Resuelto Алексей Мельников asked hace 54 años • 7 respuestas

Sé que en Shiny Server Pro hay una función de control de contraseña. La pregunta es que Shiny tiene la función contraseñaInput(), que es como textInput() ¿Alguien ha pensado en cómo hacer lo siguiente?

1) Iniciar la aplicación solo después de ingresar la contraseña correcta 2) Iniciar la parte de la aplicación después de ingresar la contraseña correcta (por ejemplo, tengo algunas pestañas en shinydashboard y quiero acceder a una de ellas solo mediante contraseña)

¡Gracias!

Aceptado

EDITAR 2019: ahora podemos usar el paquete shinymanagerpara hacer esto: el invactivityscript debe detener la página de inicio de sesión después de 2 minutos de inactividad para no desperdiciar recursos:

library(shiny)
library(shinymanager)

inactivity <- "function idleTimer() {
var t = setTimeout(logout, 120000);
window.onmousemove = resetTimer; // catches mouse movements
window.onmousedown = resetTimer; // catches mouse movements
window.onclick = resetTimer;     // catches mouse clicks
window.onscroll = resetTimer;    // catches scrolling
window.onkeypress = resetTimer;  //catches keyboard actions

function logout() {
window.close();  //close the window
}

function resetTimer() {
clearTimeout(t);
t = setTimeout(logout, 120000);  // time is in milliseconds (1000 is 1 second)
}
}
idleTimer();"


# data.frame with credentials info
credentials <- data.frame(
  user = c("1", "fanny", "victor", "benoit"),
  password = c("1", "azerty", "12345", "azerty"),
  # comment = c("alsace", "auvergne", "bretagne"), %>% 
  stringsAsFactors = FALSE
)

ui <- secure_app(head_auth = tags$script(inactivity),
                 fluidPage(
                   # classic app
                   headerPanel('Iris k-means clustering'),
                   sidebarPanel(
                     selectInput('xcol', 'X Variable', names(iris)),
                     selectInput('ycol', 'Y Variable', names(iris),
                                 selected=names(iris)[[2]]),
                     numericInput('clusters', 'Cluster count', 3,
                                  min = 1, max = 9)
                   ),
                   mainPanel(
                     plotOutput('plot1'),
                     verbatimTextOutput("res_auth")
                   )
                   
                 ))

server <- function(input, output, session) {
  
  result_auth <- secure_server(check_credentials = check_credentials(credentials))
  
  output$res_auth <- renderPrint({
    reactiveValuesToList(result_auth)
  })
  
  # classic app
  selectedData <- reactive({
    iris[, c(input$xcol, input$ycol)]
  })
  
  clusters <- reactive({
    kmeans(selectedData(), input$clusters)
  })
  
  output$plot1 <- renderPlot({
    palette(c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3",
              "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"))
    
    par(mar = c(5.1, 4.1, 0, 1))
    plot(selectedData(),
         col = clusters()$cluster,
         pch = 20, cex = 3)
    points(clusters()$centers, pch = 4, cex = 4, lwd = 4)
  })
  
}


shinyApp(ui = ui, server = server)

ingrese la descripción de la imagen aquí

Publicación original: Voy a responder el punto 1 y, para el punto 2, simplemente puedes ampliar mi ejemplo. Siguiendo este ejemplo , cifre la contraseña con md5 para Shiny-app. puedes hacer lo siguiente:

  1. Cree 2 páginas y, si el usuario ingresa el nombre de usuario y la contraseña correctos, podrá renderUIutilizarlos htmlOutputpara generar su página.
  2. Puedes diseñar la posición del cuadro con nombre de usuario y contraseña tagscomo lo hice yo y colorearlos si quieres también usandotags$style

Luego puede examinar más a fondo la página real y especificar qué se debe crear como resultado de los diferentes usuarios. También puedes consultar los cuadros emergentes de JavaScript.

EDITAR 2018: también eche un vistazo al ejemplo aquí https://shiny.rstudio.com/gallery/authentication-and-database.html

Ejemplo de portada

rm(list = ls())
library(shiny)

Logged = FALSE;
my_username <- "test"
my_password <- "test"

ui1 <- function(){
  tagList(
    div(id = "login",
        wellPanel(textInput("userName", "Username"),
                  passwordInput("passwd", "Password"),
                  br(),actionButton("Login", "Log in"))),
    tags$style(type="text/css", "#login {font-size:10px;   text-align: left;position:absolute;top: 40%;left: 50%;margin-top: -100px;margin-left: -150px;}")
  )}

ui2 <- function(){tagList(tabPanel("Test"))}

ui = (htmlOutput("page"))
server = (function(input, output,session) {
  
  USER <- reactiveValues(Logged = Logged)
  
  observe({ 
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (input$Login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          Id.username <- which(my_username == Username)
          Id.password <- which(my_password == Password)
          if (length(Id.username) > 0 & length(Id.password) > 0) {
            if (Id.username == Id.password) {
              USER$Logged <- TRUE
            } 
          }
        } 
      }
    }    
  })
  observe({
    if (USER$Logged == FALSE) {
      
      output$page <- renderUI({
        div(class="outer",do.call(bootstrapPage,c("",ui1())))
      })
    }
    if (USER$Logged == TRUE) 
    {
      output$page <- renderUI({
        div(class="outer",do.call(navbarPage,c(inverse=TRUE,title = "Contratulations you got in!",ui2())))
      })
      print(ui)
    }
  })
})

runApp(list(ui = ui, server = server))
Pork Chop avatar Mar 11 '2015 21:03 Pork Chop

Tuve la misma pregunta, me topé con la respuesta inicial anterior (usando dos UI diferentes) y me resultó demasiado difícil implementarla. Aparentemente, ha habido otros usuarios en SO con problemas similares para implementar la solución anterior.

He creado una solución mucho más sencilla utilizando la pestaña Agregar/eliminar y {shinyjs}. Así es como funciona. Podría ayudar a aquellos que no quieran trabajar con dos funciones de interfaz de usuario separadas.

  1. Cree una pestaña de inicio de sesión donde los usuarios puedan iniciar sesión. Todas las demás pestañas aún no se muestran, ni tampoco la barra lateral.
  2. Cuando el inicio de sesión sea exitoso: agregue las pestañas que realmente desea mostrar, elimine la pestaña de inicio de sesión (ya no es necesaria) y muestre la barra lateral con {shinyjs}.

Proporciono un ejemplo simple a continuación. Además, agregué algunas funciones que no son necesarias, como un historial de usuario que cuenta y limita el número de intentos de inicio de sesión, un registro de usuario y un controlador de mensajes, etc. Comenté esas funciones para simplificar las cosas, pero si estás interesado, echa un vistazo. Tenga en cuenta que las funciones adicionales deben ejecutarse en un servidor.

El único inconveniente de no utilizar shiny server pro es la falta de conexión https, que debe agregarse con otra solución alternativa si es realmente necesario.

Documenté un ejemplo simple y un enfoque con características adicionales en GitHub. Se puede encontrar una versión funcional de este último en shinyapps.io .

A continuación publico el código de la versión más simple de la aplicación centrándose en el inicio de sesión.

Los nombres de usuario y contraseñas necesarios para iniciar sesión son los siguientes:

    username   password
    user123    loginpassword1
    user456    loginpassword2

En una aplicación real, deberían almacenarse como hashes en el servidor.

library("shiny")
library("shinyjs")
library("stringr")


# in case you want to send error messages when login is wrong
# add this to the /www folder in your shiny app (shiny server) as message-handler.js file
#
# // This recieves messages of type "testmessage" from the server.
# Shiny.addCustomMessageHandler("testmessage",
#                               function(message) {
#                                   alert(JSON.stringify(message));
#                               }
# );

shinyApp(

ui = fluidPage(
    
    useShinyjs(),  # Set up shinyjs
    
    # Layout mit Sidebar
    sidebarLayout(
        
        ## Sidebar -----
        shinyjs::hidden(
            div(id = "Sidebar", sidebarPanel(
                
                # > some example input on sidebar -----
                conditionalPanel(
                    condition = "input.tabselected > 1",
                    dateRangeInput(inputId = "date",
                                   label = "Choose date range",
                                   start = "2018-06-25", end = "2019-01-01",
                                   min = "2018-06-25", max = "2019-01-01",
                                   startview = "year")) 
                
            ))), # closes Sidebar-Panel
        
        # Main-Panel ------
        mainPanel(
            
            tabsetPanel(
                
                # > Login -------
                tabPanel("Login",
                         value = 1,
                         br(),
                         textInput("username", "Username"),
                         passwordInput("password", label = "Passwort"),
                         # If you want to add custom javascript messages
                         # tags$head(tags$script(src = "message-handler.js")),
                         actionButton("login", "Login"),
                         textOutput("pwd")
                         
                ), # closes tabPanel
                
                id = "tabselected", type = "pills"
                
            )  # closes tabsetPanel      
            
        )  # closes mainPanel                      
        
    ) # closes sidebarLayout
    
), # closes fluidPage


# Server ------
server = function(input, output, session){
    
    user_vec <- c("user123" = "loginpassword1",
                  "user456" = "loginpassword2")
    
    # I usually do run the code below on a real app  on a server
    # user <- reactiveValues(his = readRDS(file = "logs/user_his.rds"),
    #                        log = readRDS(file = "logs/user_log.rds"),
    #                        vec = readRDS(file = "logs/user_vec.rds"))
    #
    # where user_his is defined as follows
    # user_his <- vector(mode = "integer", length = length(user_vec))
    # names(user_his) <- names(user_vec)
    
    
    observeEvent(input$login, {
        
        if (str_to_lower(input$username) %in% names(user_vec)) { # is username in user_vec?
        
        # Alternatively if you want to limit login attempts to "3" using the user_his file
        # if (str_to_lower(input$username) %in% names(user$vec[user$his < 3])) {
            
            if (input$password == unname(user_vec[str_to_lower(input$username)])) {
            
                # nulls the user_his login attempts and saves this on server
                # user$his[str_to_lower(input$username)] <- 0
                # saveRDS(user$his, file = "logs/user_his.rds")
                
                # Saves a temp log file
                # user_log_temp <- data.frame(username = str_to_lower(input$username),
                #                            timestamp = Sys.time())
                
                # saves temp log in reactive value
                # user$log <- rbind(user$log, user_log_temp)
                
                # saves reactive value on server
                # saveRDS(user$log, file = "logs/user_log.rds")
                
                
                # > Add MainPanel and Sidebar----------
                shinyjs::show(id = "Sidebar")
                
                appendTab(inputId = "tabselected",
                          
                          tabPanel("Tab 1",
                                   value = 2
                                   
                          ) # closes tabPanel,
                          
                )
                
                appendTab(inputId = "tabselected",
                          
                          tabPanel("Tab 2",
                                   value = 3

                          ) # closes tabPanel,
                )
                
                appendTab(inputId = "tabselected",

                          tabPanel("Tab 3",
                                   
                                   value = 4
                                   
                          ) # closes tabPanel         
                )
                
                removeTab(inputId = "tabselected",
                          target = "1")
                
            } else { # username correct, password wrong
                
                # adds a login attempt to user_his 
                # user$his[str_to_lower(input$username)] <- user$his[str_to_lower(input$username)] + 1
                
                # saves user_his on server
                # saveRDS(user$his, file = "logs/user_his.rds")
                
                # Messge which shows how many log-in tries are left
                #
                # session$sendCustomMessage(type = 'testmessage',
                #                           message = paste0('Password not correct. ',
                #                                            'Remaining log-in tries: ',
                #                                            3 - user$his[str_to_lower(input$username)]
                #                           )
                # )
                
                
            } # closes if-clause
            
        } else { #  username name wrong or more than 3 log-in failures 
            
            # Send error messages with javascript message handler
            #
            # session$sendCustomMessage(type = 'testmessage',
            #                           message = paste0('Wrong user name or user blocked.')                          
            # )
            
        } # closes second if-clause
        
    }) # closes observeEvent
    
    
} # Closes server
) # Closes ShinyApp
TimTeaFan avatar Jun 11 '2019 20:06 TimTeaFan

Agregando a la primera respuesta, también puedes cifrar tu contraseña fácilmente combinando shinymanager con el paquete scrypt. Para hacerlo, primero puede generar una contraseña y aplicarle un hash:

library(scrypt)
password <- hashPassword("ice")
password #copy this hashed output and then paste it in your app's code

Ahora, si tomamos el ejemplo anterior, todo lo que necesita hacer para modificarlo es crear un objeto que tenga su valor hash ( no el raw ) y establecer el is_hashed_passwordargumento dentro de las credenciales en TRUE.

Puede acceder a la aplicación (nombre de usuario: 1) sin tener su contraseña sin formato almacenada en el script.

library(shiny)
library(shinymanager)
library(scrypt)

inactivity <- "function idleTimer() {
var t = setTimeout(logout, 120000);
window.onmousemove = resetTimer; // catches mouse movements
window.onmousedown = resetTimer; // catches mouse movements
window.onclick = resetTimer;     // catches mouse clicks
window.onscroll = resetTimer;    // catches scrolling
window.onkeypress = resetTimer;  //catches keyboard actions

function logout() {
window.close();  //close the window
}

function resetTimer() {
clearTimeout(t);
t = setTimeout(logout, 120000);  // time is in milliseconds (1000 is 1 second)
}
}
idleTimer();"

password <- "c2NyeXB0ABAAAAAIAAAAAVYhtzTyvRJ9e3hYVOOk63KUzmu7rdoycf3MDQ2jKLDQUkpCpweMU3xCvI3C6suJbKss4jrNBxaEdT/fBzxJitY3vGABhpPahksMpNu/Jou5"

# data.frame with credentials info
credentials <- data.frame(
  user = c("1", "fanny", "victor", "benoit"),
  password = password,
  is_hashed_password = TRUE,
  # comment = c("alsace", "auvergne", "bretagne"), %>% 
  stringsAsFactors = FALSE
)

ui <- secure_app(head_auth = tags$script(inactivity),
                 fluidPage(
                   # classic app
                   headerPanel('Iris k-means clustering'),
                   sidebarPanel(
                     selectInput('xcol', 'X Variable', names(iris)),
                     selectInput('ycol', 'Y Variable', names(iris),
                                 selected=names(iris)[[2]]),
                     numericInput('clusters', 'Cluster count', 3,
                                  min = 1, max = 9)
                   ),
                   mainPanel(
                     plotOutput('plot1'),
                     verbatimTextOutput("res_auth")
                   )
                   
                 ))

server <- function(input, output, session) {
  
  result_auth <- secure_server(check_credentials = check_credentials(credentials))
  
  output$res_auth <- renderPrint({
    reactiveValuesToList(result_auth)
  })
  
  # classic app
  selectedData <- reactive({
    iris[, c(input$xcol, input$ycol)]
  })
  
  clusters <- reactive({
    kmeans(selectedData(), input$clusters)
  })
  
  output$plot1 <- renderPlot({
    palette(c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3",
              "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"))
    
    par(mar = c(5.1, 4.1, 0, 1))
    plot(selectedData(),
         col = clusters()$cluster,
         pch = 20, cex = 3)
    points(clusters()$centers, pch = 4, cex = 4, lwd = 4)
  })
  
}


shinyApp(ui = ui, server = server)
J.Sabree avatar Aug 11 '2021 21:08 J.Sabree

para el panel Shiny esto también puede ayudar

library(shiny)
library(shinydashboard)
library(DT)
library(shinyjs)
library(sodium)

# Main login screen
loginpage <- div(id = "loginpage", style = "width: 500px; max-width: 100%; margin: 0 auto; padding: 20px;",
                 wellPanel(
                   tags$h2("LOG IN", class = "text-center", style = "padding-top: 0;color:#333; font-weight:600;"),
                   textInput("userName", placeholder="Username", label = tagList(icon("user"), "Username")),
                   passwordInput("passwd", placeholder="Password", label = tagList(icon("unlock-alt"), "Password")),
                   br(),
                   div(
                     style = "text-align: center;",
                     actionButton("login", "SIGN IN", style = "color: white; background-color:#3c8dbc;
                                 padding: 10px 15px; width: 150px; cursor: pointer;
                                 font-size: 18px; font-weight: 600;"),
                     shinyjs::hidden(
                       div(id = "nomatch",
                           tags$p("Oops! Incorrect username or password!",
                                  style = "color: red; font-weight: 600; 
                                            padding-top: 5px;font-size:16px;", 
                                  class = "text-center"))),
                     br(),
                     br(),
                     tags$code("Username: myuser  Password: mypass"),
                     br(),
                     tags$code("Username: myuser1  Password: mypass1")
                   ))
)

credentials = data.frame(
  username_id = c("myuser", "myuser1"),
  passod   = sapply(c("mypass", "mypass1"),password_store),
  permission  = c("basic", "advanced"), 
  stringsAsFactors = F
)

header <- dashboardHeader( title = "Simple Dashboard", uiOutput("logoutbtn"))

sidebar <- dashboardSidebar(uiOutput("sidebarpanel")) 
body <- dashboardBody(shinyjs::useShinyjs(), uiOutput("body"))
ui<-dashboardPage(header, sidebar, body, skin = "blue")

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

  login = FALSE
  USER <- reactiveValues(login = login)

  observe({ 
    if (USER$login == FALSE) {
      if (!is.null(input$login)) {
        if (input$login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          if(length(which(credentials$username_id==Username))==1) { 
            pasmatch  <- credentials["passod"][which(credentials$username_id==Username),]
            pasverify <- password_verify(pasmatch, Password)
            if(pasverify) {
              USER$login <- TRUE
            } else {
              shinyjs::toggle(id = "nomatch", anim = TRUE, time = 1, animType = "fade")
              shinyjs::delay(3000, shinyjs::toggle(id = "nomatch", anim = TRUE, time = 1, animType = "fade"))
            }
          } else {
            shinyjs::toggle(id = "nomatch", anim = TRUE, time = 1, animType = "fade")
            shinyjs::delay(3000, shinyjs::toggle(id = "nomatch", anim = TRUE, time = 1, animType = "fade"))
          }
        } 
      }
    }    
  })

  output$logoutbtn <- renderUI({
    req(USER$login)
    tags$li(a(icon("fa fa-sign-out"), "Logout", 
              href="javascript:window.location.reload(true)"),
            class = "dropdown", 
            style = "background-color: #eee !important; border: 0;
                    font-weight: bold; margin:5px; padding: 10px;")
  })

  output$sidebarpanel <- renderUI({
    if (USER$login == TRUE ){ 
      sidebarMenu(
        menuItem("Main Page", tabName = "dashboard", icon = icon("dashboard"))
      )
    }
  })

  output$body <- renderUI({
    if (USER$login == TRUE ) {
      tabItem(tabName ="dashboard", class = "active",
              fluidRow(
                box(width = 12, dataTableOutput('results'))
              ))
    }
    else {
      loginpage
    }
  })

  output$results <-  DT::renderDataTable({
    datatable(iris, options = list(autoWidth = TRUE,
                                   searching = FALSE))
  })

}

runApp(list(ui = ui, server = server), launch.browser = TRUE)
Seyma Kalay avatar Dec 29 '2019 06:12 Seyma Kalay