zoukankan      html  css  js  c++  java
  • [译]Rails中使用Authlogic进行用户鉴定(Rails Authentication with Authlogic)

    近日,发现Authlogic这个gem,看上去比devise清爽了不少,于是打算用用,于是发现了这篇文章,遂翻译一下当做学习。


    今天,我很高兴的为大家介绍 AuthLogic,一个简单地Ruby下的用户鉴定解决方案,它的作者是 Ben Johnson.

    AuthLogic 它很隐蔽和底层。它不依赖于生成的代码 (举个例子,例如 Devise) ——相反地,它提供了一些工具使得你可以用来安装你喜欢的方式构建你的应用。感觉上,, AuthLogic和 Sorcery有些相似。

    下面让我们创建一个Rails应用,它可以允许用户注册、登入登出,同时还可以重置密码。在实现这些的同时,我们探讨一些 AuthLogic的特性和设置。阅读本文之后,你已经准备好在你的实际应用中使用AuthLogic了。

    你可以在GitHub.找到本文的代码。

    演示应用在这里: sitepoint-authlogic.herokuapp.com.

    开始

    我将为你们演示的demo app 基于Rails 4.1,当然AuthLogic是支持 Rails 3, 甚至是 Rails 2的 (需要独立的分支 separate branch ).

    创建一个新的应用,名为 “Logical” ,并不生成标准的测试套件:

    $ rails new Logical -T

    添加如下的gems:

    Gemfile

    [...]
    gem 'bootstrap-sass'
    gem 'authlogic', '3.4.6'
    [...]

    然后执行下述命令:

    $ bundle install

    如果你愿意,就可以使用 Bootstrap的样式了:

    stylesheets/application.scss

    @import 'bootstrap-sprockets';
    @import 'bootstrap';

    然后,编辑view的layout:

    views/layouts/application.html.erb

    [...]
    <nav class="navbar navbar-inverse">
      <div class="container">
        <div class="navbar-header">
          <%= link_to 'Logical', root_path, class: 'navbar-brand' %>
        </div>
        <div id="navbar">
          <ul class="nav navbar-nav">
            <li><%= link_to 'Home', root_path %></li>
          </ul>
        </div>
      </div>
    </nav>
    
    <div class="container">
      <% flash.each do |key, value| %>
        <div class="alert alert-<%= key %>">
          <%= value %>
        </div>
      <% end %>
    
      <%= yield %>
    </div>
    [...]

    现在,创建静态页面的controller:

    pages_controller.rb

    class PagesController < ApplicationController
      def index
      end
    end

    然后,创建响应的view:

    views/pages/index.html.erb

    <div class="jumbotron">
    <div class="container">
      <h1>Welcome!</h1>
      <p>Sign up to get started.</p>
      <p>
        <%= link_to '#', class: 'btn btn-primary btn-lg' do %>
          Sign Up &raquo;
        <% end %>
      </p>
    </div>

    接下来路由:

    config/routes.rb

    [...]
    root to: 'pages#index'
    [...]

    好,打完收工,接下来就让我们将AuthLogic 集成进来!

    安装设置AuthLogic

    Models

    使用AuthLogic需要包含两个model:一个基础的我们通常称为User,一个特殊的需要继承自Authlogic::Session::Base,并称之为UserSession (你可以看看 这篇说明 ,快速地了解它的工作原理)。

    第一步,当然是新建一个数据库的 migration:

    $ rails g model User email:string crypted_password:string password_salt:string persistence_token:string

    这里 是所有可能需要的字段列表)

    然后需要在生成的migration中添加下面一行:

    migrations/xxx_create_users.rb

    [...]
    add_index :users, :email, unique: true
    [...]

    执行migrate:

    $ rake db:migrate

    这几个字段 emailcrypted_password和 password_salt都实际上是可选的。AuthLogic并不关心你使用什么手段鉴定用户——使用LDAP 亦或 OAuth 2,, 这里是一份列表 list of AuthLogic “add-ons” 用来支持不同的鉴定方式,然而,它们中的许多已经没人维护了。

    顺便说一下,你可以添加一个login字段,这样 AuthLogic将会用它来替代email。

    字段persistence_token 是必须的。 AuthLogic 用它来存放用户的会话安全码。

    下面修改User model:

    models/user.rb

    [...]
    acts_as_authentic
    [...]

    这已经给 User model添加了 AuthLogic 功能了。 acts_as_authentic 将会接受一个block,用来重载默认的设置项(比如口令验证规则——我们将会在本文最后一段来讨论这个话题)。

    现在我们要创建一个全新的,特殊的model:

    models/user_session.rb

    class UserSession < Authlogic::Session::Base
    end

    Windows用户特别说明

    There is a pretty serious error that Windows users are likely to encounter when using AuthLogic with Ruby 2.1 or 2.2 (I have not tested with Ruby 1.9). Full discussion can be found on GitHub, but, in short, this error is related to SCrypt, a gem that implements secure password hashing algorithm. AuthLogic uses SCrypt as a default crypto provider, but on Windows it constantly returns segmentation error and the server crashes.

    The quickest way is to use another provider – AuthLogic offers a handful of them.

    In this demo I will stick to SHA512, so tweak the model:

    models/user.rb

    [...]
    acts_as_authentic do |c|
        c.crypto_provider = Authlogic::CryptoProviders::Sha512
    end
    [...]

    Controllers 和Helpers

    让我们完成管理用户的controller :

    users_controller.rb

    class UsersController < ApplicationController
      def new
        @user = User.new
      end
    
      def create
        @user = User.new(users_params)
        if @user.save
          flash[:success] = "Account registered!"
          redirect_to root_path
        else
          render :new
        end
      end
    
      private
    
      def users_params
        params.require(:user).permit(:email, :password, :password_confirmation)
      end
    end

    看上去,这就是一个基本的controller,响应用的注册动作。注意一定要同时permit password 和 password_confirmation 两个属性,因为,默认的,AuthLogic将会检查他们是否一致。

    下面是管理用户登入登出的controller:

    user_sessions_controller.rb

    class UserSessionsController < ApplicationController
      def new
        @user_session = UserSession.new
      end
    
      def create
        @user_session = UserSession.new(user_session_params)
        if @user_session.save
          flash[:success] = "Welcome back!"
          redirect_to root_path
        else
          render :new
        end
      end
    
      def destroy
        current_user_session.destroy
        flash[:success] = "Goodbye!"
        redirect_to root_path
      end
    
      private
    
      def user_session_params
        params.require(:user_session).permit(:email, :password, :remember_me)
      end
    end

    正如你看到的,我们使用 UserSession model 来鉴定用户,它自动持久化用户会话,因此controller本身显得非常清爽。

    current_user_session? 是什么?它是一个helper 方法:

    application_controller.rb

    [...]
    private
    
    def current_user_session
      return @current_user_session if defined?(@current_user_session)
      @current_user_session = UserSession.find
    end
    
    def current_user
      return @current_user if defined?(@current_user)
      @current_user = current_user_session && current_user_session.user
    end
    
    helper_method :current_user_session, :current_user
    [...]

    UserSession.find 自动使用 persistence_token 来找到当前的会话。

    我还同时添加了 current_user来容易的访问当前用户的记录

    路由

    我们设置了一下路由:

    config/routes.rb

    [...]
    resources :users, only: [:new, :create]
    
    resources :user_sessions, only: [:create, :destroy]
    
    delete '/sign_out', to: 'user_sessions#destroy', as: :sign_out
    get '/sign_in', to: 'user_sessions#new', as: :sign_in
    [...]

    视图

    最后,我们来搞定视图。

    首先,是 layout:

    views/layouts/application.html.erb

    [...]
    <nav class="navbar navbar-inverse">
      <div class="container">
        <div class="navbar-header">
          <%= link_to 'Logical', root_path, class: 'navbar-brand' %>
        </div>
        <div id="navbar">
          <ul class="nav navbar-nav">
            <li><%= link_to 'Home', root_path %></li>
          </ul>
          <ul class="nav navbar-nav pull-right">
            <% if current_user %>
              <li><span><%= current_user.email %></span></li>
              <li><%= link_to 'Sign Out', sign_out_path, method: :delete %></li>
            <% else %>
              <li><%= link_to 'Sign In', sign_in_path %></li>
            <% end %>
          </ul>
        </div>
      </div>
    </nav>
    [...]

    在这里,我简单的写了一个顶部的菜单。

    然后是 “注册”页面:

    views/users/new.html.erb

    <div class="page-header"><h1>Register</h1></div>
    
    <%= form_for @user do |f| %>
      <%= render 'shared/errors', object: @user %>
    
      <div class="form-group">
        <%= f.label :email %>
        <%= f.email_field :email, class: 'form-control' %>
      </div>
    
      <div class="form-group">
        <%= f.label :password %>
        <%= f.password_field :password, class: 'form-control' %>
      </div>
    
      <div class="form-group">
        <%= f.label :password_confirmation %>
        <%= f.password_field :password_confirmation, class: 'form-control' %>
      </div>
    
      <%= f.submit 'Register', class: 'btn btn-primary btn-lg' %>
    <% end %>

    AuthLogic 自动验证 e-mail,验证passwords是否一致并且最少4个字母长。你可以在model/user.rb 中重新定义这些——我们将会进一步讨论他。

    下面,添加共享的显示错误的部分:

    views/shared/_errors.html.erb

    <% if object.errors.any? %>
      <div class="panel panel-warning errors">
        <div class="panel-heading">
          <h5><i class="glyphicon glyphicon-exclamation-sign"></i> Found errors while saving</h5>
        </div>
    
        <ul class="panel-body">
          <% object.errors.full_messages.each do |msg| %>
            <li><%= msg %></li>
          <% end %>
        </ul>
      </div>
    <% end %>

    还有,不要忘记在 index.html.erb添加到注册页面的链接:

    views/pages/index.html.erb

    <div class="jumbotron">
      <div class="container">
        <h1>Welcome!</h1>
        <p>Sign up to get started.</p>
        <p>
          <%= link_to new_user_path, class: 'btn btn-primary btn-lg' do %>
            Sign Up &raquo;
          <% end %>
        </p>
      </div>
    </div>

    接下来,终于要写登录的视图了:

    views/user_sessions/new.html.erb

    <div class="page-header"><h1>Sign In</h1></div>
    
    <%= form_for @user_session do |f| %>
      <%= render 'shared/errors', object: @user_session %>
    
      <div class="form-group">
        <%= f.label :email %>
        <%= f.email_field :email, class: 'form-control' %>
      </div>
    
      <div class="form-group">
        <%= f.label :password %>
        <%= f.password_field :password, class: 'form-control' %>
      </div>
    
      <div class="form-group">
        <%= f.label :remember_me %>
        <%= f.check_box :remember_me %>
      </div>
    
      <%= f.submit "Log in!", class: 'btn btn-primary btn-lg' %>
    <% end %>

    到这里,你最好启动server来注册你的的第一个用户。

    保存额外的信息

    AuthLogic支持一些“魔法属性” 他们会自动的产生。你可以用这些来保存更多的用户相关信息,比如最后的登录日期或者IP地址。

    创建一个新的migration:

    $ rails g migration add_magic_columns_to_users

    打开生成的文件并修改:

    xxx_add_magic_columns_to_users.rb

    class AddMagicColumnsToUsers < ActiveRecord::Migration
      def change
        add_column :users, :login_count, :integer, :null => false, :default => 0
        add_column :users, :failed_login_count, :integer, :null => false, :default => 0
        add_column :users, :last_request_at, :datetime
        add_column :users, :current_login_at, :datetime
        add_column :users, :last_login_at, :datetime
        add_column :users, :current_login_ip, :string
        add_column :users, :last_login_ip, :string
      end
    end

    然后执行migrate:

    $ rake db:migrate

    接下来,为了简单起见,我们在首页上显示这些信息(如果用户登录了):

    views/pages/index.html.erb

    <% if current_user %>
      <div class="page-header"><h1>Welcome back!</h1></div>
    
      <h2>Some info about you...</h2>
    
      <div class="well well-lg">
        <ul>
          <li>Login count: <%= current_user.login_count %></li>
          <li>Failed login count: <%= current_user.failed_login_count %></li>
          <li>Last request: <%= current_user.last_request_at %></li>
          <li>Current login at: <%= current_user.current_login_at %></li>
          <li>Last login at: <%= current_user.last_login_at %></li>
          <li>Current login IP: <%= current_user.current_login_ip %></li>
          <li>Last login IP: <%= current_user.last_login_ip %></li>
        </ul>
      </div>
    <% else %>
        [...]
    <% end %>

    登录然后看看显示结果。这些信息可能会很有用,比如你想要了解你的用户访问网站的频率。

    重置密码

    Users tend to forget their passwords, therefore it is crucial to present them with a way to reset it. AuthLogic provides you with a tool to add this functionality, as well.

    The author of AuthLogic suggests using a simple mechanism where a user first enters an e-mail, then receives a link to update the password, and then follows the link to actually set the new password. This link contains a special “perishable” token that has to be reset.

    Therefore we need to add a new field called perishable_token to the users table. Note that we do not call it something like reset_password_token. AuthLogic does not dictate that this token can be used only for password resetting – you may use it, for example, to activate users’ accounts.

    Apart from perishable_token, AuthLogic also supports single_access_token that is ideal for APIs – it provides access, but does not persist. Read more here.

    Okay, so create and apply a new migration:

    $ rails g migration add_perishable_token_to_users perishable_token:string
    $ rake db:migrate

    Obviously we need a special controller to manage password resets and a mailer.

    Start with controller:

    password_resets_controller.rb

    class PasswordResetsController < ApplicationController
      def new
      end
    
      def create
        @user = User.find_by_email(params[:email])
        if @user
          @user.deliver_password_reset_instructions!
          flash[:success] = "Instructions to reset your password have been emailed to you."
          redirect_to root_path
        else
          flash[:warning] = "No user was found with that email address"
          render :new
        end
      end
    
      def edit
        @user = User.find_by(perishable_token: params[:id])
      end
    
      def update
        @user = User.find_by(perishable_token: params[:id])
        if @user.update_attributes(password_reset_params)
          flash[:success] = "Password successfully updated!"
          redirect_to root_path
        else
          render :edit
        end
      end
    
      private
    
      def password_reset_params
        params.require(:user).permit(:password, :password_confirmation)
      end
    end

    new is the action that will be called when a user clicks on the “Forgot your password?” link.

    create processes the sent data and tries to fetch a user by e-mail. If this user is found, password reset instructions are sent to their e-mail.

    edit is called when a user visits the link that was sent to the provided e-mail. This link contains our perishable token that is employed to find the user record. The edit page contains another form to enter a new password.

    Inside the update action, fetch the user and update their password.

    We’ll need the routes, as well:

    config/routes.rb

    ...
    resources :password_resets, only: [:new, :create, :edit, :update]
    ...

    Now, add a new method to your model:

    models/user.rb

    [...]
    def deliver_password_reset_instructions!
      reset_perishable_token!
      PasswordResetMailer.reset_email(self).deliver_now
    end
    [...]

    reset_perishable_token! is a method supplied by AuthLogic – it simply sets perishable_token to a new random value and saves the record.

    We also have to create our new mailer:

    mailers/application_mailer.rb

    class ApplicationMailer < ActionMailer::Base
      default from: "from@example.com"
      layout 'mailer'
    end

    mailers/password_reset_mailer.rb

    class PasswordResetMailer < ApplicationMailer
      def reset_email(user)
        @user = user
        mail(to: @user.email, subject: 'Password reset instructions')
      end
    end

    views/layouts/mailer.text.erb

    <%= yield %>

    views/password_reset_mailer/reset_email.text.erb

    Here is your link to reset password: <%= edit_password_reset_url(@user.perishable_token) %>.

    Also don’t forget to set the default URL options:

    config/environments/development.rb

    config.action_mailer.default_url_options = { host: '127.0.0.1:3000' }

    Please note that in the development environment, e-mails won’t actually be sent, but you’ll be able to see their contents inside the console. Also note that my demo app won’t send e-mails, but it’s easy to setup for a production environment. Read more here.

    Lastly, update the views to include the “Forgot your password?” link:

    views/user_sessions/new.html.erb

    <div class="page-header"><h1>Sign In</h1></div>
    
    <%= form_for @user_session do |f| %>
      [...]
      <%= f.submit "Log in!", class: 'btn btn-primary btn-lg' %>
      <br/><br/>
      <%= link_to 'Forgot your password?', new_password_reset_path, class: 'btn btn-sm btn-default' %>
    <% end %>

    Now go ahead and check how this is working!

    调整默认设置

    正如我曾经说过的, AuthLogic提供了一些默认设置,但是你能够容易的改变它们,只有简单地给acts_as_authentic传递一个代码块。想要了解更多,你可以浏览 文档,我将在这里重点说明一些:

    暴力破解Brute Force

    默认情况下,如果你的user表中存在字段 failed_login_count ,AuthLogic 会试图防止暴力破解 (某些人使用程序和字典来猜测密码)。这里有两项设置:

    • consecutive_failed_logins_limit (默认 50次) – 允许的连续登陆失败次数。设置为0,将会禁止防暴力破解保护。
    • failed_login_ban_for (默认值:2小时) ——用户账户将会被锁定多长时间。设置为0 ,账户将永久锁定。

    了解更多?看 这里

    HTTP Basic Auth

    AuthLogic 支持HTTP basic authentication,默认是打开的。

    Password配置

    有 不少设置 用来改变默认的字段来保存login 和password信息,同样用户会话查找方法也有不少设置。

    同样地这篇 列出了密码验证和一致性验证相关的设置项。

    登出和超时

    你可以让AuthLogic 在用户登录一段时间后标记为登出了。只要设置logout_on_timeout为 true 然后使用stale? 来检查用户是否需要重新登录。

    点击 这里.了解更多信息。

    你也可以通过设置logged_in_timeout 来确定用户是否登录了。 更多信息.

    E-mail 配置

    Visit this page to learn about various options related to e-mails (field to store e-mail, validation rules, and more).

    There are many more options that you can use for AuthLogic customization, so be sure to browse the documentation.

    总结

    本文中,我们对AuthLogic稍作了解并且构建了一个简单的应用。学习观察不同的用户鉴定解决方案并进行比较是非常有趣的,你认为呢?

    你首选哪一种用户鉴定解决方案,为什么? 你是否曾经遇到某些限制或者特殊情况导致你不得不开发你自己的用户鉴定系统?有的话,请共享一下你的经验!

    欢迎回馈,像通常那样。感谢你浪费时间陪伴我到这里,再会。

    作者信息就不译了:)

  • 相关阅读:
    已知: 每个飞机只有一个油箱, 飞机之间可以相互加油(注意是相互,没有加油机) 一箱油可供一架飞机绕地球飞半圈,问题:为使至少一架飞机绕地球一圈回到起飞时的飞机
    简易vector的实现
    简单的内存池实现
    归并排序,递归与非递归
    堆排序
    位运算
    二叉树的建立,以及非递归遍历
    “云端融合”思想的自我摸索(很不靠谱)
    linux android开发环境搭建
    Android系统架构及内核简介
  • 原文地址:https://www.cnblogs.com/dajianshi/p/12444448.html
Copyright © 2011-2022 走看看