如何让非签名用户只访问受限制的页面一次?

时间:2013-08-28 20:06:33

标签: ruby-on-rails authentication devise

我有一个Rails 3.2应用程序,需要用户登录(current_user)才能访问子域名的事件页面。我正在使用设计进行身份验证。

如果我提供了直接链接,有没有办法允许用户一次性访问活动页面?然后我会希望他们在尝试访问不同的活动页面时被提示登录(或注册),或者如果他们离开并在将来的某个日期回到同一个活动页面。

我在railscasts上观看了来宾帐户剧集,但似乎用户可以继续以访客身份登录,而无需注册此方法。

这是我的事件控制器代码:

def show
    @event = Event.find_by_name(request.subdomain)
    if params[:id].present?
      @event = Event.find(params[:id])
    end
    # if @event.present? and @event.videos.present?
      @video = Video.find_by_id(params[:video]) || @event.videos.first
    # else 
    #   @video = @event.videos.first
    # end
    # @json = Event.all.to_gmaps4rails 
    if @user = current_user
    else 
      flash[:alert] = "Please sign in first"
      redirect_to sign_up_url(:subdomain => false)
    end
end

感谢您提供任何帮助/建议......

编辑:只是提供更多背景信息:

  1. 我每周都会尝试将很多用户带到这一个活动页面,如果我只是发送一个链接,我不确定将单独的哈希硬编码到网址中是否可行/切实可行通过社交媒体和电子邮件等。

  2. 由于用户点击此链接并直接转到相关页面,因此没有触发操作来创建单独的访客模型,所以我认为答案必须是基于会话的,添加一些列到现有用户模型,但确保每个用户只使用一次。我想也许IP地址可以工作?

5 个答案:

答案 0 :(得分:3)

最有先见之明的解决方案是为访客用户创建一个会话变量,其中包含一个布尔值,表示他们是否访问过该页面(如果他们没有访问过,则启用访问权限,反之亦然):

# in your controller
before_filter :check_guest, :only => :show

private

def check_guest
  # if user isn't logged in
  if current_user.nil? 
    # if user has already viewed, redirect
    if session[:viewed] == true
      flash[:alert] = "Please sign in first"
      redirect_to sign_up_url(:subdomain => false)
    # if user hasn't viewed, allow access, but flag as having viewed
    else
      session[:viewed] = true
    end
  end
end

虽然使用会话是解决此特定问题的事实上的方法,但其实施存在限制:

  • 会话必须保持有效,以便跟踪访客访问权限
  • 特定于浏览器(因此浏览器上的多个用户将共享同一会话)

答案 1 :(得分:3)

一个解决方案,而不是设置会话变量,是用url对一次性哈希进行编码,如下所示:

http://site.com/event?access_id=AKJHDA23fdsank

然后,在访问该事件时,将删除访问代码。这将需要管理数据库中的访问代码,但具有万无一失的附加好处。

你可以简单地创建一个api url来生成这些链接,以简化共享过程。

其他一切都很容易被骗。

答案 2 :(得分:1)

您可以使用feature_flags gem设置布尔值:

https://rubygems.org/gems/feature_flags

答案 3 :(得分:0)

我不知道预先构建的解决方案,但它不应该太难实现。我做了类似的事情:

class GuestPass < ActiveRecord::Base

  belongs_to :event

  # the guest_passes table has a `code` column that contains a long arbitrary string token
end

class Event < ActiveRecord::Base

  has_many :guest_passes

  def authenticate_guest_pass!(token)
    return false unless token
    guest_passes.where(code: token).first
  end

end

class EventsController < ActiveRecord::Base

  before_action :load_event, only: [ :show ]
  before_action :validate_guest_pass, unless: current_user

  private

  def load_event
    @event = Event.find(params[:id])
  end

  def validate_guest_pass
    pass = @event.authenticate_guest_pass!(params[:pass])
    if pass
      pass.delete
    else
      # redirect to sign in or whatever
    end
  end

end

然后,您只需生成GuestPass条记录,并将其粘贴在您提供的链接中:它们看起来像/events/1?pass=xyzzy

(这里的代码可以使用一些清理;我并不是说它很漂亮。但它是第一次通过。)

答案 4 :(得分:0)

这是我对zeantsoi解决方案的略微修改版本,其附加好处是即使用户退出浏览器会话也会持续存在。

def check_guest
  #if user is logged in
  unless current_user 
    @event = Event.find_by_name(request.subdomain)
    #if user has already viewed, redirect
    if @event.guest_ip_address == request.remote_ip
      flash[:alert] = "Please sign in first"
      redirect_to sign_up_url(:subdomain => false)
    #if user hasn't viewed, allow access, but flag as having viewed and store their ip address in the event model so it knows not to show the page again unless current_user
    else
      session[:viewed] = request.remote_ip
      @event.guest_ip_address = session[:viewed]
      @event.save!
    end
  end