Sorcery的安装和设置 Sorcery功能提供类似于Devise,地址,devise支持多模型,但是sorcery只是支持单模型,一般情况下,一个模型就可以了,在这个模型中通过字段属性决定模型的角色,相对而言,sorcery是轻量级的,devise是重量级别的。 安装和查看:
#安装gem
gem 'sorcery'
#显示gem的安装地址
bundle show sorcery
#使用下面方式可以查看多少功能能使用rails g功能
rails g -h #显示rails g sorcery:install
#使用下面方式可以查看sorcery:install安装哪些模块
rails g sorcery:install -h
#默认使用user这个model
rails g sorcery:install
#默认生成user模型,为模型添加字段就是给user模型添加字段
rails g migration add_username_to_users username:string
#改变默认的user模型为person模型,后面必须为Person单数形式,数据库中为people复数形式
rails generate sorcery:install --model Person
#添加相关模块,一般情况添加下面模块就可以了,不过需要下一节的config配置。
rails g sorcery:install remember_me reset_password user_activation --only-submodules
配置文件/config/initializers/sorcery.rb,这些地方需要配置才行,目的是把激活功能删除,减少配置的复杂度 需要提前设置好用重置密码需要用到的类
rails g mailer user
配置如下
user.user_activation_mailer = nil
user.activation_mailer_disabled = true
user.prevent_non_active_users_to_login = false
user.reset_password_mailer = UserMailer #通过上面代码生成
Sorcery具体使用 sorcery只有针对密码的加密功能,但是没有针对密码的校验和密码一致性的校验。执行上面的模块建立方式:
rails g sorcery:install remember_me reset_password user_activation --only-submodules
config/initializers/sorcery.rb参考如上的配置方式。 建立model/user.rb文件代码
class User < ApplicationRecord
authenticates_with_sorcery!
attr_accessor :password, :password_confirmation
validates :name, presence: { message: "不能为空" }
validates :email, uniqueness: { message: "已经存在该邮箱"}
validates :password, presence: { message: "密码不能为空" }
if: :need_validate_password
validates :password_confirmation, presence: { message: "密码确认不能为空" }
if: :need_validate_password
validates :password, confirmation: { message: "密码不一致" }
if: :need_validate_password
validates :password, length: { minimum: 6, too_short: "密码不少于6个字符" }
if: :need_validate_password
private
def need_validate_password
self.new_record? ||
(!self.password.nil? or !self.password_confirmation.nil?)
end
end
1、上面的代码说明,必须在user.rb文件添加authenticates_with_sorcery!方法,这个是sorcery默认必须放在User类中执行的类方法。默认建立的两个虚拟属性是password和password_confirmation两个属性,sorcery中保存的加密字段是crypted_password,sorcery针对解决的是password的加密,而没有针对password验证的过程,所以上面的代码很多是针对密码的验证。 2、针对need_validate_password的方法的说明,这个方法在第一次见的时候比较迷惑,经过探索,自己的理解如下。正常的web页面中针对密码的更改和针对用户名等一些信息的修改是放在不同的用户界面中,同时password和password_confirmation是虚拟属性(区别于实际属性,更新的情况下会从数据库取值,而虚拟属性不会从数据库中取值),每一次用户属性更新的情况下,如果没有need_validate_password的方法,那么每次都会执行相应的验证操作,而正常的web页面其实密码更新和用户其他信息的更新是分开的。所以体现了need_validate_password方法的必要性。 3、这个方法说明,关于密码的验证的前提条件,针对新的字段,或者是针对密码进行更新的情况下(密码进行更新意味着密码肯定不为空)。
关于sorcery方法的说明:sorcery自定义的方法,一般是应用于model层和controller层,但是例外是helper方法,既可以应用于controller层中,也可以应用于view层中。比如如下两个:
logged_in?
current_user
关于注册controller文件users_controller.rb的说明: 下面的内容分别对应的是上面在刚开始创建的时候建立的模块。
class UsersController < ApplicationController
def new
@user = User.new
end
#用户注册
def create
@user = User.new(params.require(:user)
.permit(:email, :password, :password_confirmation))
if @user.save
flash[:notice] = '注册成功,请登录'
redirect_to new_session_path
else
render action: :new
end
end
#用户密码修改
def change_password
if current_user.valid_password?(params[:old_password])
current_user.password_confirmation = params[:password_confirmation]
if current_user.change_password!(params[:password])
render json: {status: 'ok', msg: '密码修改成功'}
else
render json: {status: 'error', msg: current_user.errors.messages.values.flatten}
end
else
render json: {status: 'error', msg: '旧密码不正确'}
end
end
#用户发送忘记密码邮件,这里需要对邮件进行配置
def send_forget_password_email
@user = User.find_by(email: params[:email])
if @user
@user.deliver_reset_password_instructions!
render json: {status: 'ok', msg: "重置密码的链接已发送到上面的邮箱"}
else
render json: {status: 'error', msg: '指定邮箱不存在'}
end
end
#密码重置
def reset_password
@user = User.load_from_reset_password_token(params[:token])
if @user
@user.password_confirmation = params[:password_confirmation]
if @user.change_password!(params[:password])
render json: {status: 'ok', msg: '密码修改成功'}
else
render json: {status: 'error', msg: @user.errors.messages.values.flatten}
end
else
render json: {status: 'error', msg: 'token不正确或者已过期'}
end
end
end
关于用户登录controller文件sessions_controller.rb文件的创建
class SessionsController < ApplicationController
before_action :auth_user, except: [:destroy]
def new
end
def create
#login是sorcery的登录方法
if user = login(params[:email], params[:password])
flash[:notice] = '登陆成功'
redirect_to root_path
else
flash[:notice] = "邮箱或者密码错误"
redirect_to new_session_path
end
end
def destroy
#logout是sorcery的退出方法
logout
flash[:notice] = "退出登陆"
redirect_to root_path
end
private
def auth_user
#logged_in?是sorcery的判断登录方法
if logged_in?
redirect_to root_path
end
end
end
登录界面
<h1>Sessions#new</h1>
<%= form_tag sessions_path, method: :post, class: "form-horizontal" do %>
<div class="form-group">
<div class="col-lg-12">邮箱</div>
<div class="col-lg-12">
<%= text_field_tag :email, nil, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="col-lg-12">密码</div>
<div class="col-lg-12">
<%= password_field_tag :password, nil, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="col-lg-12">
<div class="pull-right">
<a href="<%= new_user_path %>">注册</a>
</div>
</div>
</div>
<div class="form-group">
<div class="col-lg-12">
<input type="submit" name="sign_submit" value="登陆" class="col-xs-12 btn btn-primary">
</div>
</div>
<% end -%>
注册界面
<h1>Users#new</h1>
<div>
<% unless @user.errors.empty? %>
<ul>
<% @user.errors.messages.values.flatten.each do |error| %>
<li><%= error %></li>
<% end -%>
</ul>
<% end -%>
</div>
<%= form_for :user, url: users_path, method: :post, html: { class: 'form-horizontal', id: "user_form"} do |f| %>
<div class="form-group">
<div class="col-lg-12">邮箱 *</div>
<div class="col-lg-12">
<%= f.text_field :email, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="col-lg-12">密码 *</div>
<div class="col-lg-12">
<%= f.password_field :password, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="col-lg-12">密码确认 *</div>
<div class="col-lg-12">
<%= f.password_field :password_confirmation, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="col-lg-12">
<input type="submit" name="create_user_submit" value="创建账户" class="col-xs-12 btn btn-primary">
</div>
</div>
<% end -%>