类似于Kiosk的Web应用程序 - 如何“锁定”应用程序?

时间:2014-04-28 22:43:52

标签: ruby-on-rails security authentication authorization

问题:什么是“锁定”Rails Web应用程序的好方法,因此用户必须输入凭据才能解锁(但是这样用户仍然登录到应用程序而无法使用该应用程序解锁它?)

我在做什么

我正在构建一个任务管理应用程序,其行为类似于多个用户使用的自助服务终端。用户可以使用他们的电子邮件地址和密码(完整凭证)登录应用程序,但一旦他们进入,他们就可以通过从列表中选择他们的名字并输入四位数的PIN(简单凭证)来“交换”用户。

为什么有两种凭据?

这是一个多租户应用,用户使用“完整凭据”只需进入应用并选择租户。一旦他们进入,他们将经常更换新员工轮班和其他员工离职,因为多个员工将共享一个工作站。

我没有强制用户重复输入他们的“完整”凭据(通过完全注销/登录),而是创建了一个名称/ PIN集凭据,可以用来轻松交换。一旦他们使用完整凭据登录并选择了他们的租户,该应用程序会提供用户姓名的自动完整列表,因此从列表中选择一个人的姓名,输入PIN码并交换用户非常容易。

请注意,此“交换”功能已构建并正常运行。

为什么我需要锁定应用程序?

当用户登录应用程序和租户时,他们可能需要离开应用程序才能执行其他工作。没有锁定屏幕,他们只有两个选择:完全注销,以便下一个用户必须输入“完整”凭据才能使用该应用程序,或者只是让他们的帐户登录以供任何人使用。

我想让他们在应用程序离开时单击按钮以“锁定”应用程序,以便用户可以“登录”应用程序而不允许其他用户访问其他用户的帐户。一旦他们点击“锁定”他们所能做的只是输入名称/ PIN来解锁,或点击“退出”以完全注销。

以下是典型用例的样子:

  • Frank使用他的电子邮件地址和密码登录应用程序。
  • 他完成了一些任务。
  • Jane希望使用该应用程序,因此她通过从列表中选择她的名字并输入她的四位数PIN来“交换”。
  • 现在Jane是登录用户,这意味着她可以完成任务,编辑她的个人资料以及其他活动。
  • Jane完成了一些任务,需要在商店中做其他事情,所以她点击了屏幕上的“锁定”按钮。
  • 该应用现已锁定,需要使用身份验证(名称和PIN),或者用户可以点击“退出”以完全退出应用。屏幕上只显示“应用程序已锁定。请输入您的姓名和PIN码以解锁它。”

我理解的选项

似乎有两种方法可以到这里:

  1. 某种JavaScript
  2. 某种使用会话变量或cookie的控制器逻辑
  3. JavaScript使我感到紧张,因为它非常难以理解。我想确保该应用实际上是安全的。

    所以我一直在搞乱使用会话变量的方法。这是我一直在尝试的:

    • 用户已针对特定租户的应用进行身份验证
    • 用户点击“锁定”按钮
    • 在Sessions控制器中调用“lock_ui”操作
    • 该操作设置了几个会话变量(session [:locked],session [:locked_by],session [:locked_at])
    • 该应用随后会重定向到已锁定的会话,显示“已锁定。输入名称和要解锁的PIN码”表单,除非它已解锁或用户点击“退出”。
    • 用户输入有效的名称/ PIN后,会删除这些会话变量,并且应用程序正常运行。

    有关我如何做到这一点的任何建议,以确保其安全吗?

    我被挂断的东西:

    • 我是否应该使用某种应用程序控制器过滤器(在此之前或之前)来检查应用程序是否已被锁定?如果应用程序被锁定,应用程序控制器会做什么? (它是否会调用其他操作,直接呈现“锁定”页面或其他内容?)
    • 或者这是应该在视图层中处理的东西吗?例如,我可以更新我的应用程序布局,使其具有“if locked_ui渲染'锁定'屏幕,否则产生”。

    关于我目前如何设置的一些注意事项:

    • 这是一个使用托管在heroku上的Ruby 1.9.3的Rails 4.1.0应用程序。
    • 我正在使用CanCan进行授权
    • 我正在使用multi-tenancy with scopes(我不使用子域名)
    • 每个用户只有一个可以访问多个租户的帐户
    • 我见过JQuery BlockUI,但它看起来更像虚荣而不是功能性安全设备

1 个答案:

答案 0 :(得分:1)

这就是我最终建立的目标。

回答我自己的问题:

我是否应该使用某种应用程序控制器过滤器(在此之前或之前)检查应用程序是否被锁定?我在应用程序控制器中使用before_filter来检查UI是否是锁定。如果它被锁定,我会重定向到“锁定”屏幕。然后我将skip_before_filter添加到我的会话控制器中的相关操作中(当用户导航到“锁定”屏幕时,基本上忽略了before_filter,以及Sessions控制器中的相关锁定/解锁操作。

或者这是应该在视图层中处理的东西吗?在视图层中,我大多只需要创建实际的“锁定”屏幕(用户输入凭据以解锁应用程序)并且我确保在UI被锁定时隐藏导航元素(因此用户不会混淆为什么他们点击“编辑个人资料”并且不要离开锁定屏幕)。

我正在使用cookie来记录UI的当前状态(已锁定或未锁定)。以下是一些代码段:

application_controller.rb

before_filter :confirm_unlocked_ui

private

def confirm_unlocked_ui
  if signed_in? && locked_ui?
    redirect_to locked_path
  end
end

sessions_helper.rb

def locked_ui?
  session[:locked] == '1'
end

def lock_ui
  session[:locked] = '1'
  session[:locked_by] = current_user.id
  session[:locked_at] = Time.zone.now
end

def unlock_ui
  session.delete(:locked)
  session.delete(:locked_by)
  session.delete(:locked_at)
end

sessions_controller.rb

def lock
  session[:return_to] = params[:return_to] if params[:return_to]
  lock_ui

  redirect_to locked_path
end

def locked
  #essentially just a view
end

def unlock
  user = User.find_by_id(params[:session][:unlock_user_id]) 
  if user && user.authenticate_with_pin(params[:session][:pin])
    cookies[:auth_token] = user.auth_token
    unlock_ui
    redirect_back_or root_path
  else      
    flash.now[:error] = "Invalid PIN."
    render 'locked'
  end
end

def destroy
  sign_out
  unlock_ui
  redirect_to root_path
end

我真的不知道这个解决方案有多“好”,我在这里发布的一个原因是看看其他人是否提出了更好的想法,或者看到潜在的问题,我是如何做到这一点的。< / p>

如果您尝试这样做,可能需要考虑一些更微妙的事情:

  • 我正在使用CanCan进行授权,我已经把它留在了这里。如果您有基于角色的授权或类似的东西,请务必检查这是否适用于每个角色。
  • 请注意,我允许用户“退出”,如果他们不想“解锁”。我使用'destroy'动作执行此操作我的会话控制器,并且在我退出之后重定向它之前确保'unlock_ui'。如果我没有这样做,那么下次他们从该浏览器登录时,应用程序仍然会“锁定”。
  • locked_pa​​th是Sessions#locked(基本上只是一个视图/表单)的别名,它响应get请求。
  • 会话#Lock还响应获取请求。
  • 会话#解锁是从“锁定”视图提交表单时调用的内容。