努力用 rails 后端、react + redux 前端实现设计用户角色

Struggling to implement Devise user roles with rails backend, react + redux frontend

我正在构建一个应用程序,其中有两种类型的用户,memberbusiness_event。我正在努力实施这一点。我有一个用户注册表单,无论他们是“会员”还是“企业或活动”,他们都可以从下拉菜单中 select。当我尝试注册特定角色时,我的 rails 控制台始终显示“角色”是“成员”(我分配的默认值)。

在我的 rails 后端,我添加了设计 gem 和这个迁移:

class AddRoleToUsers < ActiveRecord::Migration[6.1]
  def change
    add_column :users, :role, :integer
  end
end

然后我将 User 模型更改为以下内容:

class User < ApplicationRecord
    has_secure_password
    enum role: [:member, :business_event]
    after_initialize :set_default_role, :if => :new_record?

    def set_default_role
      self.role ||= :member
    end
end

在我的 React 前端,这是我想用来注册用户的表单:

import React from 'react'
import { connect } from 'react-redux'
import { updateSignupForm } from "../../actions/signupForm.js"
import { signup } from "../../actions/currentUser.js"

const Signup = ({ signupFormData, updateSignupForm, signup }) => {

    const handleUserInfoInputChange = event => {
      const { name, value } = event.target
      const updatedFormInfo = {
        ...signupFormData,
        [name]: value
      }
      updateSignupForm(updatedFormInfo)
    }
  
     
  
    const handleSubmit = event => {
      event.preventDefault()
       
      signup(signupFormData)
    }
  
    return (
      <div>
        <h1>Sign Up </h1>

      <form onSubmit={handleSubmit}>
        <div className="ui input">
        <select>
        <option value={signupFormData.role}>Member</option>
        <option value={signupFormData.role}>Business or Event</option>
        </select>
         <input placeholder="username" value={signupFormData.username} name="username" type="text" onChange={handleUserInfoInputChange} /> 
        <input placeholder="password" value={signupFormData.password} name="password" type="text" onChange={handleUserInfoInputChange} /> 
         <input placeholder="email" value={signupFormData.email} name="email" type="text" onChange={handleUserInfoInputChange}/><br/><br/>

        <button className="button button-signup" type="submit" value="Sign Up" > Sign up</button>

        </div>
      </form>
      </div>
    
    )
  }
  
  const mapStateToProps = state => {
    return {
      signupFormData: state.signupForm
    }
  }
  
  export default connect(mapStateToProps, { updateSignupForm, signup } )(Signup)

在我的 signupForm reducer 中,我决定将“role”的初始值设置为“0”,因为它在后端是一个整数。我不确定那是否是我出错的地方:

  const initialState = {
    username: "",
    password: "",
    email: "",
    role: 0
  }
  
  export default (state=initialState, action) => {
    switch (action.type) {
      case "UPDATE_SIGNUP_FORM":
        return action.formData
      case "RESET_SIGNUP_FORM":
        return initialState
      default:
        return state
    }
  }

我不确定为什么用户总是恢复到默认角色“成员”。任何帮助将不胜感激。

在添加其他功能之前,您真的应该只专注于正确安装 Devise 并为您的基本身份验证系统添加测试。收起你的工作并回溯。

首先运行生成器生成配置文件:

rails g devise:install

然后设置您的模型:

rails g devise user

这增加了以下内容:

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
end

您不希望 has_secure_password 这是 Rails 自己的密码加密宏来构建您自己的身份验证系统。 Devise database_authenticatable 模块可以满足您的需求。现在写 integration tests ,其中涵盖注册和登录而不涉及反应。当涉及到保持理智时,这将得到肯定的回报。

现在您可以真正开始使用角色功能了。

在数据库级别添加具有默认值的枚举列:

class AddRoleToUsers < ActiveRecord::Migration[6.1]
  def change
    add_column :users, :role, :integer, default: 0
  end
end

使用回调设置属性默认值是一种过时且老套的方法,存在许多已知缺陷。使用数据库默认值,除非你真的需要评估应用程序级别的默认值 - 在这种情况下你想通过 attributes api.

将枚举宏添加到模型中:

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  enum role: [:member, :business_event]
end

如果您真的希望能够在注册用户时通过参数设置角色,您需要在 Devise 中将参数列入白名单:

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:role])
  end
end

但是您需要仔细考虑这样做的安全隐患,因为它可能允许恶意用户通过简单地通过 cURL 发送请求或操纵输入来授予自己更高的权限。这可能现在不相关,但如果您添加管理员角色,请记住这一点。

角色的整数值也是一个只有模型知道的实现细节。你的前端和控制器等其他一切都应该只知道键:

const initialState = {
  username: "",
  password: "",
  email: "",
  role: "member"
}