我的应用有三种不同的用户类型(即设计模型):SuperAdminUser
,AdminUser
和StandardUser
。每个用户,无论何种类型,都有一个首选项页面,他们可以在其中更新多个属性(例如:email
,:password
,:username
等),其中两个属性要求用户输入其当前密码以进行更新(:email
,:password
)的属性,而其余属性不需要更新当前密码。
现在,在我的理想世界中,首选项更新将使用AJAX,以便只更改的属性将在页面中刷新而不是整个页面重新加载。目前,我的这项功能适用于不要求用户输入当前密码的所有属性,但我无法使用:email
和:password
。奇怪的是,我让它在以前的原型中工作,但是只有一个Devise用户模型,而不是三个,并且单个模型没有它自己的注册控制器覆盖Devise注册控制器,当前应用程序中有三个用户模型,但我在这些重写注册控制器中看不到会影响此问题的任何内容。
我收到的唯一错误消息是我自己的,在首选项页面上:
Sorry, but we were unable to update your password.
问题似乎是没有从表单中捕获:current_password
参数值。控制台日志语句将其显示为空字符串,同时捕获:new_password
和:new_password_confirmation
值没有问题。我还没能确定为什么会这样。作为一种替代方法,我也尝试尽可能地模仿Devise wiki中的更改密码示例(最后一个示例)(此处:https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-password),但无济于事。任何建议将不胜感激。
此外,我想要问的一个重要问题是我是否应该使用带密码属性的AJAX。也就是说,通过AJAX传递这些密码值本质上是不安全的吗?如果是这样,有没有办法安全地(例如加密)?
以下是我的相关代码:
应用/控制器/ super_admin_users / preferences_controller.rb
class SuperAdminUsers::PreferencesController < ApplicationController
include ApplicationHelper
before_action :authenticate_super_admin_user!
.
.
.
def change_password
@super_admin_user = SuperAdminUser.find_by(id: current_super_admin_user.id)
if resource.update_with_password(params.permit(:password,
:password_confirmation,
:current_password))
sign_in resource_name, self.resource, bypass: true
status_message = 'Your password has been updated successfully!'
else
status_message = 'Sorry, but we were unable to update your password.'
end
respond_to do |format|
format.json { render json: { status_message: status_message } }
end
end
def password_section_partial
@change_password_status_message = params[:change_password_status_message] || ""
render 'super_admin_users/preferences/password/password_section_partial'
end
.
.
.
end
end
应用/视图/ super_admin_users /偏好/口令/ _change_password_form.html.haml
= form_tag super_admin_user_prefs_change_password_path, id: 'prefs-change-password-form' do
.prefs-label-and-input-wrapper
#prefs-new-password-label-wrapper.prefs-input-label-wrapper
= label_tag 'password', 'New Password', id: 'prefs-new-password-label',
class: 'prefs-input-label'
#prefs-new-password-input-wrapper.prefs-input-wrapper
= password_field_tag 'password', nil, autofocus: true,
autocomplete: 'off',
id: 'prefs-new-password-input',
class: 'prefs-input'
.prefs-label-and-input-wrapper
#prefs-new-password-confirmation-label-wrapper.prefs-input-label-wrapper
= label_tag 'password_confirmation', 'New Password Confirmation', id: 'prefs-new-confirmation-password-label',
class: 'prefs-input-label'
#prefs-new-password-confirmation-input-wrapper.prefs-input-wrapper
= password_field_tag 'password_confirmation', nil, autocomplete: 'off',
id: 'prefs-new-password-confirmation-input',
class: 'prefs-input'
.prefs-label-and-input-wrapper
#prefs-current-password-label-wrapper.prefs-input-label-wrapper
= label_tag 'current_password', 'Current Password', id: 'prefs-current-password-label',
class: 'prefs-input-label'
#prefs-current-password-input-wrapper.prefs-input-wrapper
= password_field_tag 'current_password', nil, autocomplete: 'off',
id: 'prefs-current-password-input',
class: 'prefs-input'
#prefs-change-password-form-buttons-wrapper
#prefs-change-password-form-submit-btn-wrapper
= link_to 'Update', 'javascript:;', id: 'prefs-change-password-form-submit-btn', class: 'btn btn-sm btn-success btn-submit'
-#%input{ id: 'prefs-change-password-form-submit-btn', class: 'btn btn-sm btn-success', type: 'submit', value: 'Update' }
#prefs-change-password-form-cancel-btn-wrapper
= link_to 'Cancel', 'javascript:;', id: 'prefs-change-password-form-cancel-btn', class: 'btn btn-sm btn-secondary btn-cancel'
应用/视图/ super_admin_users /偏好/口令/ _change_password_status_message.html.haml
#prefs-change-password-status-message.prefs-status-message= change_password_status_message
应用/视图/ super_admin_users /偏好/口令/ _password_section.html.haml
#prefs-password-section-header.prefs-section-header
#prefs-password-headline-wrapper
.prefs-section-headline Password
#prefs-password-edit-btn-wrapper
= link_to 'Edit', 'javascript:;', id: 'prefs-password-edit-btn', class: 'btn btn-sm btn-primary btn-edit'
#prefs-change-password-form-wrapper.prefs-form-wrapper.no-display
= render partial: 'super_admin_users/preferences/password/change_password_form'
#prefs-change-password-status-message-wrapper.prefs-status-message-wrapper.no-display
应用/视图/ super_admin_users /偏好/口令/ password_section_partial.js.haml
$('#prefs-change-password-status-message-wrapper').empty();
$('#prefs-change-password-form-wrapper').addClass('no-display');
$('#prefs-change-password-form').trigger('reset');
- if @change_password_status_message.present?
$('#prefs-change-password-status-message-wrapper').append("#{ escape_javascript(render(partial: 'super_admin_users/preferences/password/change_password_status_message', locals: { change_password_status_message: @change_password_status_message })) }");
$('#prefs-change-password-status-message-wrapper').removeClass('no-display');
$('#prefs-password-edit-btn').removeClass('no-display');
应用/控制器/ application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include ApplicationHelper
include DeviseParamSanitizerOverrides
def get_current_user_type
respond_to do |format|
format.json { render json: { current_user_type: resource_name.to_s } }
end
end
def get_current_environment
respond_to do |format|
format.json { render json: { current_environment: Rails.env } }
end
end
protected
def devise_parameter_sanitizer
if resource_class.to_s == 'SuperAdminUser'
SuperAdminUser::ParameterSanitizer.new(SuperAdminUser, :super_admin_user, params)
elsif resource_class.to_s == 'AdminUser'
AdminUser::ParameterSanitizer.new(AdminUser, :admin_user, params)
elsif resource_class.to_s == 'StandardUser'
StandardUser::ParameterSanitizer.new(StandardUser, :standard_user, params)
end
end
end
应用/控制器/关切/ devise_param_sanitizer_overrides.rb
module DeviseParamSanitizerOverrides
extend ActiveSupport::Concern
class SuperAdminUser::ParameterSanitizer < Devise::ParameterSanitizer
def initialize(*)
super
permit(:sign_up, keys: [:email, :first_name, :last_name, :username])
permit(:sign_in, keys: [:email, :username])
permit(:account_update, keys: [:current_password, :email, :first_name, :last_name, :time_zone, :username])
end
end
.
.
.
end
应用/助手/ application_helper.rb
module ApplicationHelper
def resource
if super_admin_user_signed_in?
@super_admin_user ||= SuperAdminUser.new
elsif admin_user_signed_in?
@admin_user ||= AdminUser.new
elsif standard_user_signed_in?
@standard_user ||= StandardUser.new
end
end
def resource_name
if super_admin_user_signed_in?
:super_admin_user
elsif admin_user_signed_in?
:admin_user
elsif standard_user_signed_in?
:standard_user
end
end
def resource_class
if super_admin_user_signed_in?
SuperAdminUser
elsif admin_user_signed_in?
AdminUser
elsif standard_user_signed_in?
StandardUser
end
end
def devise_mapping
if super_admin_user_signed_in?
@devise_mapping ||= Devise.mappings[:super_admin_user]
elsif admin_user_signed_in?
@devise_mapping ||= Devise.mappings[:admin_user]
elsif standard_user_signed_in?
@devise_mapping ||= Devise.mappings[:standard_user]
end
end
def resource_authenticated_root
if super_admin_user_signed_in?
authenticated_super_admin_user_root
elsif admin_user_signed_in?
authenticated_admin_user_root
elsif standard_user_signed_in?
authenticated_standard_user_root
end
end
end
应用/资产/ Javascript角/ preferences.js
var currentUserType;
var currentEnvironment;
getCurrentUserType('/get_current_user_type'
).done(function(getCurrentUserTypeResponse) {
currentUserType = getCurrentUserTypeResponse.current_user_type;
});
getCurrentEnvironment('/get_current_environment'
).done(function(getCurrentEnvironmentResponse) {
currentEnvironment = getCurrentEnvironmentResponse.current_environment;
});
$(document).ready(function() {
$('#prefs-password-edit-btn').click(function (e) {
$('#prefs-password-edit-btn').addClass('no-display');
$('#prefs-change-password-form').trigger('reset');
$('#prefs-change-password-form-wrapper').removeClass('no-display');
});
$(function() {
return $('body').on('click', '#prefs-change-password-form-submit-btn', function() {
$('#prefs-change-password-form-wrapper').addClass('no-display');
changePassword('/' + currentUserType + 's/preferences/change_password?password=' + $('#prefs-new-password-input').val() +
'&password_confirmation=' + $('#prefs-new-password-confirmation-input').val() +
'¤t_password=' + $('#prefs-current-password-input').val()
).done(function(changePasswordResponse) {
$.ajax({url: '/' + currentUserType + 's/preferences/password_section_partial?change_password_status_message=' + changePasswordResponse.status_message});
});
});
});
$(function() {
return $('body').on('click', '#prefs-change-password-form-cancel-btn', function() {
$('#prefs-change-password-form-wrapper').addClass('no-display');
$('#prefs-change-password-form').trigger('reset');
$('#prefs-password-edit-btn').removeClass('no-display');
});
});
function getCurrentUserType(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
if (currentEnvironment === 'development') {
alert('AJAX Get Current User Type Error');
}
})
}
function getCurrentEnvironment(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
alert('AJAX Get Current Environment Error');
})
}
function changePassword(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
if (currentEnvironment === 'development') {
alert('AJAX Change Password Error');
}
})
}
配置/ routes.rb中
Rails.application.routes.draw do
get '/get_current_user_type', to: 'application#get_current_user_type'
get '/get_current_environment', to: 'application#get_current_environment'
.
.
.
devise_for :super_admin_users, controllers: { registrations: 'super_admin_users/registrations' }
authenticated :super_admin_user do
root to: 'super_admin_users/dashboard#index', as: 'authenticated_super_admin_user_root'
end
as :super_admin_user do
get 'super_admin_users/preferences/password_section_partial', to: 'super_admin_users/preferences#password_section_partial',
as: :super_admin_user_password_section_partial
get 'super_admin_users/preferences/change_password', to: 'super_admin_users/preferences#change_password',
as: :super_admin_user_prefs_change_password
end
end