楚新元 | All in R

Welcome to R Square

分享 Shiny 的用户和角色权限管理方案

楚新元 / 2023-03-09


最近做了一个自动生成制式化 Word 文档的 Shiny app1,顺便用 shinyauthr 包做了一个用户登录界面,但是要实现用户修改密码的功能困扰了我好久,主要问题是 session 一旦起来后只修改配置文件里的用户密码是不行了,因为内存中的密码没有变,后来我终于想到了解决方案,把读取配置文件放在 server function 里面,每次修改配置文件密码后立马通过 session$reload() 重启会话,这样就会重新读取配置文件的用户信息,从而刷新内存中的密码信息,问题得到解决。

用户登录的问题解决了,现在要给不同的用户分配不同的访问权限。首先生成配置文件,这个配置文件包括所有用户账号、密码、有效期和访问权限信息。这个可以用如下代码生成:

# 加载 R 包
library(RSQLite)

# 连接数据库
con = dbConnect(SQLite(), "./config/config.sqlite")

# 创建用户信息
user_group = data.frame(
  name = c("管理员", "张三", "李四", "王五", "赵六"),
  user = c("admin", "user1", "user2", "user3", "user4"),
  password = c(
    "admin", "666666", "666666", "666666", "666666"
  ),
  permissions = c(
    "admin", "standard", "standard", "standard", "standard"
  ),
  expire = c(
    "9999-12-31", "2100-12-31", "2100-12-31",
    "2100-12-31", "2023-12-31"
  ),
  page1 = c("yes", "yes", "no", "yes", "yes"),
  page2 = c("yes", "no", "yes", "yes", "yes")
)

# 用户信息写入到配置文件
dbWriteTable(
  conn = con,
  name = "user_group",
  value = user_group,
  overwrite = TRUE
)

# 关闭连接
dbDisconnect(config)

配置文件里包含 5 个用户,代表了 5 种典型的情况:

Shiny app 在获取配置文件的用户信息后,只需要在每个菜单前用 if() 进行判断即可,下面是控制用户菜单访问权限的处理逻辑:

shinyServer(function(input, output, session) {
  
  # 打开配置文件
  config = dbConnect(SQLite(), "./config/config.sqlite")
  
  # 查询用户组信息
  user_group = dbGetQuery(config, "select * from user_group")
  
  # 关闭数据库
  dbDisconnect(config)
  
  #-------------------------------------------------------------------------
  
  # 定义凭证信息
  credentials = loginServer(
    id = "login",
    data = user_group,
    user_col = user,
    pwd_col = password,
    log_out = reactive(logout_init())
  )
  
  # 用户退出
  logout_init = logoutServer(
    id = "logout",
    active = reactive(credentials()$user_auth)
  )
  
  # 捕获登录用户信息
  user_info = reactive({
    credentials()$info
  })
  
  # 判断用户状态
  my_condition = reactive({
    all(
      credentials()$user_auth, 
      as.Date(user_info()$expire) >= Sys.Date()
    )
  })
  
  #-------------------------------------------------------------------------
  
  # 账号密码正确且没有过期时,登录后后打开侧边栏
  observe({
    if(my_condition()) {
      removeClass(
        selector = "body", 
        class = "sidebar-collapse")
    } else {
      addClass(
        selector = "body", 
        class = "sidebar-collapse")
    }
  })
  
  #-------------------------------------------------------------------------
  
  # 设置侧边菜单栏 UI
  output$sidebar = renderMenu({
    req(my_condition())
    sidebarMenu(
      id = "tabs",
      
      if (user_info()$page1 == "yes") {      
        menuItem(
          "图示页面",
          tabName = "page1",
          icon = icon("chart-simple")
        )
      },
      
      if (user_info()$page2 == "yes") {      
        menuItem(
          "报表页面",
          tabName = "page2",
          icon = icon("table")
        )
      },      
      
      if (user_info()$permissions == "admin") {
        menuItem(
          "参数配置",
          tabName = "setting",
          icon = icon("wrench")
        )
      },
      
      menuItem(
        "修改密码",
        tabName = "resetpasswd",
        icon = icon("key")
      ), 
      
      menuItem(
        "软件说明",
        tabName = "about",
        icon = icon("podcast")
      )
    )
  })

})

下面是一个带用户和角色权限管理功能的 Shiny app 示例:


  1. 需要体验的读者可以给我发邮件,我需要先配置用户登陆信息。 ↩︎