Devise,devise_token_auth和ng-token-auth

时间:2017-03-01 21:52:49

标签: ruby-on-rails angularjs authentication devise

我有点陷入困境,我无法找到解决方案。我有一个rails站点,它使用前端的devise和api的devise_token_auth。前端使用服务器端呈现页面和api调用的组合来向用户显示数据。 如果我使用纯角度登录,登录表格将最终工作(通常2-3次提交):

%div{'ng-controller'=>'logInCtrl'}
  %h2 Log In
  %div{:layout=>'column'}
    %div{:flex=>20}
    %div{:flex=>60, :layout=>'column'}
      = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
        %div{:layout=>'column'}
          %md-input-container
            =f.label :login
            %input{'ng-model'=>'loginForm.login', :autofocus => 'true'}
          %md-input-container
            = f.label :password
            %input{:type=>'password', 'ng-model'=>'loginForm.password', :autocomplete => 'off'}

          %md-input-container
            %md-button.md-raised.md-primary{'ng-click'=>'submitMe()'}
              -#{:type=>'submit'}
              %md-icon.mdi.mdi-account-key
              Log In

:coffee
  myApp.controller 'logInCtrl', ($scope, $resource, $http, $mdDialog, $auth ) ->
    $scope.submitMe = () ->
      $auth.submitLogin($scope.loginForm).then( (resp)->
        location.replace('/users/sign_in')
      )

如果我使用标准的post方法,服务器会呈现正确的信息,但没有为ng-token-auth设置令牌。我可以在session #create上手动生成和发送令牌头:

  # POST /resource/sign_in
  def create
    super do |user|
      newAuth = user.create_new_auth_token
      response.headers.merge!(newAuth)
    end
  end

我使用这种方法的问题是ng-token-auth从不从标题中提取令牌,因为它没有发出请求。我已经找到了一种手动设置令牌标头而没有运气的方法。

- 作为一方,我不会最终转向oauth解决方案,所以我使用的任何解决方法都需要移植到那个。 - 我应该提到服务器端渲染处理设计元素以及打开和关闭功能。我还使用current_user的元素来根据用户位置设置表名子集。

1 个答案:

答案 0 :(得分:0)

经过一些研究和一段时间后,我想出了一个有效的解决方案,虽然有点hacky。

当Devise创建新会话时,将重定向调用到配置文件中指定的路由,因此设置任何变量都将丢失。第二个问题是ng-token-auth只会设置和使用在登录功能期间设置的令牌,因此不会检测到通过页面标题发送令牌(这是浏览器的限制而不是代码)。

在为我的用户尝试使用ng-token-auth和标准设计身份验证的不同迭代之后,我得出的结论是,最好首先使用设计授权用户,然后以某种方式设置带有ng-token-auth的令牌;所以我开始研究ng-token-auth在通过登录收到令牌时实际做了些什么。事实证明它设置了两个cookie:

currentConfigName |默认| | / | exp date

auth_headers | 网址编码令牌数据 | | / | exp date

现在的问题是如何将新生成的令牌传递给前端;这让我们变得比我想象的更简单。由于Rails中调用之间唯一的数据是会话数据,因此我决定在会话数据中添加一个标志,这标志着我的ApplicationController生成一个新密钥。

首先我扩展了Devise::SessionsController#create

def create
  super do |user|
    session[:token] = true
  end
end

这会将名为token的会话变量设置为True。比ApplicationController我添加:

 before_filter :set_token

  def set_token
    if session[:token] && user_signed_in?
      newAuth = current_user.create_new_auth_token
      response.headers.merge!(newAuth)
      @auth_token = newAuth
      session.delete(:token)
    else
      @auth_token = false
    end
  end

此前过滤器会查找session[:token],如果设置调用devise-token-auth' s create_new_auth_token功能,则需要登录'当前用户。此标头信息既写入传出页面标题,也分配给变量@auth_token。 最后在 views / laoyouts / applicationhtml.haml 中,此代码块将添加到%body代码

之后
- if @auth_token
  :coffee
    myApp.controller 'cookieController', ($scope, $resource, $http, $mdDialog, $auth, ipCookie ) ->
      ipCookie('auth_headers', "{\"access-token\": \"#{@auth_token['access-token']}\",\"token-type\": \"Bearer\",\"client\": \"#{@auth_token['client']}\",\"expiry\": \"#{@auth_token['expiry']}\",\"uid\": \"#{@auth_token['uid']}\"}", {path: "/",expires: 9999,expirationUnit: 'days',secure: false})
      ipCookie('currentConfigName', 'default', {path: "/",expires: 9999,expirationUnit: 'days',secure: false})
  %div{'ng-controller'=>'cookieController'}

这会添加一个空的div标记和角度控制器,它使用写入@auth_token的数据和ipCookie函数(ng-token-auth所需的)来编写必要的cookie。

一些注释:

  • 我将代码块添加到主布局页面ApplicationController,因为当用户登录我的网站时,他们可能会被重定向到两个页面中的一个,具体取决于他们的凭据。这确保了无论发送到代码令牌的页面是什么,都会生成代码令牌并插入代码块。
  • 我知道可能有更好的方法来处理cookie的生成,而不是创建一个空div并为其分配控制器。我对更优雅的解决方案持开放态度。