如何设置 angular-rails 4.2 - 设计 registration/authentication?

How to setup angular-rails 4.2 - devise registration/authentication?

你能给点建议或推荐一些与这个主题相关的资源吗?我了解如何在理论上做到这一点。但我也听说过 jwt 等。实现基于 device/angular/rails 角色的 auth/registration 的最佳实践是什么?

简短的回答是阅读这个blog post,其中详细介绍了如何最低限度地实施这个概念


这将是一个很长的代码答案,但我打算写一篇单独的博客 post 来详细介绍如何实现它...

但现在,这是我在某个项目中实现它的方式...

首先是 angular 应用程序部分,您可以使用 Satellizer 之类的东西,播放效果很好...

这里是前端应用中的angular授权模块

# coffeescript
config = (
  $authProvider
  $stateProvider
) ->
  $authProvider.httpInterceptor = true # to automatically add the headers for auth
  $authProvider.baseUrl = "http://path.to.your.api/"
  $authProvider.loginRedirect = '/profile' # front-end route after login
  $authProvider.logoutRedirect = '/' # front-end route after logout
  $authProvider.signupRedirect = '/sign_in' 
  $authProvider.loginUrl = '/auth/sign_in' # api route for sign_in
  $authProvider.signupUrl = '/auth/sign_up' # api route for sign_up
  $authProvider.loginRoute = 'sign_in' # front-end route for login
  $authProvider.signupRoute = 'sign_up' # front-end route for sign_up
  $authProvider.signoutRoute = 'sign_out' # front-end route for sign_out
  $authProvider.tokenRoot = 'data' 
  $authProvider.tokenName = 'token'
  $authProvider.tokenPrefix = 'front-end-prefix-in-localstorage'
  $authProvider.authHeader = 'Authorization'
  $authProvider.authToken = 'Bearer'
  $authProvider.storage = 'localStorage'

  # state configurations for the routes
  $stateProvider
    .state 'auth',
      url: '/'
      abstract: true
      templateUrl: 'modules/auth/auth.html'
      data:
        permissions:
          only: ['guest']
          redirectTo: 'profile'


    .state 'auth.sign_up',
      url: $authProvider.signupRoute
      views:
        'sign_up@auth':
          templateUrl: 'modules/auth/sign_up.html'
          controller: 'AuthenticationCtrl'
          controllerAs: 'vm'

    .state 'auth.sign_in',
      url: $authProvider.loginRoute
      views:
        'sign_in@auth':
          templateUrl: 'modules/auth/sign_in.html'
          controller: 'AuthenticationCtrl'
          controllerAs: 'vm'

这是satellizer的基本配置...至于认证控制器...类似下面

  @signIn = (email, password, remember_me) ->
    $auth.login
      email: email
      password: password
      remember_me: remember_me
    .then(success, error)
    return

  @signUp = (name, email, password) ->
    $auth.signup
      name: name
      email: email
      password: password
    .then(success, error)
    return

这是身份验证的基础知识


至于后端 (RoR API),您应该首先允许前端应用程序使用 CORS。并将 gem 'jwt' 添加到您的 gemfile。

其次实施 API 控制器和身份验证控制器

例如,它可能如下所示

class Api::V1::ApiController < ApplicationController
  # The API responds only to JSON
  respond_to :json
  before_action :authenticate_user!
  protected

  def authenticate_user!
    http_authorization_header?
    authenticate_request
    set_current_user
  end

  # Bad Request if http authorization header missing
  def http_authorization_header?
    fail BadRequestError, 'errors.auth.missing_header' unless authorization_header
    true
  end

  def authenticate_request
    decoded_token ||= AuthenticationToken.decode(authorization_header)
    @auth_token   ||= AuthenticationToken.where(id: decoded_token['id']).
      first unless decoded_token.nil?

    fail UnauthorizedError, 'errors.auth.invalid_token' if @auth_token.nil?
  end

  def set_current_user
    @current_user ||= @auth_token.user
  end

  # JWT's are stored in the Authorization header using this format:
  # Bearer some_random_string.encoded_payload.another_random_string
  def authorization_header
    return @authorization_header if defined? @authorization_header

    @authorization_header =
      begin
        if request.headers['Authorization'].present?
          request.headers['Authorization'].split(' ').last
        else
          nil
        end
      end
  end
end


class Api::V1::AuthenticationsController < Api::V1::ApiController
  skip_before_action :authenticate_user!, only: [:sign_up, :sign_in]

  def sign_in
    # getting the current user from sign in request
    @current_user ||= User.find_by_credentials(auth_params)
    fail UnauthorizedError, 'errors.auth.invalid_credentials' unless @current_user

    generate_auth_token(auth_params)

    render :authentication, status: 201
  end

  def sign_out
    # this auth token is assigned via api controller from headers
    @auth_token.destroy!
    head status: 204
  end
  def generate_auth_token(params)
    @auth_token = AuthenticationToken.generate(@current_user, params[:remember_me])
  end
end

AuthenticationToken 是一种用于跟踪 JWT 令牌的模型(用于像 facebook 这样的会话管理)

这里是 AuthenticationToken 模型的实现

class AuthenticationToken < ActiveRecord::Base
  ## Relations
  belongs_to :user

  ## JWT wrappers
  def self.encode(payload)
    AuthToken.encode(payload)
  end

  def self.decode(token)
    AuthToken.decode(token)
  end

  # generate and save new authentication token for the user
  def self.generate(user, remember_me = false)
    @auth_token = user.authentication_tokens.create
    @auth_token.token = AuthToken.generate(@auth_token.id, remember_me)
    @auth_token.save!
    @auth_token
  end

  # check if a token can be used or not
  # used by background job to clear the authentication collection
  def expired?
    AuthToken.decode(token).nil?
  end
end

它使用一个名为 AuthToken 的包装器来包装 JWT 功能 这是它的实现

# wrapper around JWT to encapsulate it's code
# and exception handling and don't polute the AuthenticationToken model
class AuthToken
  def self.encode(payload)
    JWT.encode(payload, Rails.application.secrets.secret_key_base)
  end

  def self.decode(token)
    payload = JWT.decode(token, Rails.application.secrets.secret_key_base)[0]
  rescue JWT::ExpiredSignature
    # It will raise an error if it is not a token that was generated
    # with our secret key or if the user changes the contents of the payload
    Rails.logger.info "Expired Token"
    nil
  rescue
    Rails.logger.warn "Invalid Token"
    nil
  end

  def self.generate(token_id, remember_me = false)
    exp = remember_me ? 6.months.from_now : 6.hours.from_now
    payload = { id: token_id.to_s, exp: exp.to_i }
    self.encode(payload)
  end
end