如何在 yii2 中使用两个不同的模型登录或切换身份 class?

How to login using two different model or switch identity class in yii2?

我想允许用户从两个不同的模型登录。

Config.php

'user' => [
        'identityClass' => 'app\models\User', //one more class here
        'enableAutoLogin' => false,
        'authTimeout' => 3600*2,
    ],

LoginForm.php

 public function rules()
{
    return [
        // username and password are both required
        [['username', 'password'], 'required'],
        // rememberMe must be a boolean value
        ['rememberMe', 'boolean'],
        // password is validated by validatePassword()
        ['password', 'validatePassword'],
    ];
}

 public function validatePassword($attribute, $params)
{
    if (!$this->hasErrors()) {
        $user = $this->getUser();

        if (!$user || !$user->validatePassword($this->password)) {
            $this->addError($attribute, Yii::t('user', 'Incorrect username or password.'));
        }
    }
}

public function login()
{
    if ($this->validate()) {
        return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
    } else {
        return false;
    }
}

public function parentLogin()
{
    // How to validate parent Login?
}

public function getUser()
{
    if ($this->_user === false) {
        $this->_user = User::findByUsername($this->username);
    }

    return $this->_user;
}

User.php

class User extends \yii\db\ActiveRecord implements IdentityInterface
{
    public static function tableName()
   {
    return 'users';
   }

   public static function findIdentity($id)
  {
    return static::findOne($id);
  }

  public static function findByUsername($username)
 {
    return static::findOne(['user_login_id' => $username]);
 }

Controller.php

 public function actionLogin()
{
    // Working
}

public function actionParentLogin()
{
    $model = new LoginForm();

    if ($model->load(Yii::$app->request->post()) && $model->parentLogin()) {

            $parent = ParentLogin::find()->where(['p_username' => $model->p_username])->one();
        if($parent){
            \Yii::$app->session->set('p_id',$parent->p_id);
            return $this->redirect(['parent-dashboard']);
        }
        else
        {
            Yii::$app->getSession()->setFlash('error', Yii::t('site', 'Incorrect username or password.'));
        }
    }
    return $this->render('parent-login', [
            'model' => $model,
        ]);
}

我不知道如何验证家长登录。我尝试了几个小时寻找解决方法,但没有成功。

我卡在 Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0); 因为用户 table 没有父登录记录。

我的问题

1) 是否可以有两个identityClass。如果是那么如何?
2) 是否可以将 ParentLogin 模型扩展到 User。如果是那么如何验证?

参考资料

How To extend user class?
Customising the CWebUser class
Custom userIdentity class in yii2

** 编辑,这个不行,你只能有一个身份class ** ** 参考 https://github.com/yiisoft/yii2/issues/5134 ** 我建议尝试以下 - 未经测试。

在您的配置中,按照您的建议添加一个额外的身份接口;

'user' => [
        'identityClass' => 'app\models\User',
        'enableAutoLogin' => false,
        'authTimeout' => 3600*2,
    ],
'parent' => [
        'identityClass' => 'app\models\Parent',
        'enableAutoLogin' => false,
        'authTimeout' => 3600*2,
    ],

您的 Parent 模型可以扩展 User 模型,它将提供与原始 User 模型相同的验证方法,或者从头开始实施 IdentityInterface .根据您在 parent table 中的列名称,我建议使用第二种方法,因为列与 User table.

不同

然后您将需要两个 loginFormsloginFormparentLoginForm,因为每种情况下的验证都不同。

然后,在您的控制器中,您可以根据需要调用相应的登录表单。

Joe Miller 有一个很好的建议,即让一个用户 class 和用户 table 中的一些布尔字段来检查用户的角色,作为 rbac 的替代方案。但由于在您的情况下这是不可能的,因此我可以向您提供以下建议(此方法已经过 half-way 测试,需要采用)。

是的,您可以拥有两个或更多身份类别,但不能同时拥有。您需要处理身份之间的切换。所以首先,我建议您稍微编辑一下 LoginForm 模型:

class LoginForm extends Model
{
    public $username;
    public $password;
    public $rememberMe = true;
    // we added this parameter to handle userModel class
    // that is responsible for getting correct user
    public $userModel;

    private $_user = false;

    /* all other methods stay same */

    /**
     * Finds user by [[username]]
     *
     * @return User|null
     */
    public function getUser()
    {
        if ($this->_user === false) {
            // calling findByUsername method dynamically
            $this->_user = call_user_func(
                [$this->userModel, 'findByUsername'], 
                $this->username
            );
        }

        return $this->_user;
    }
}

现在在控制器中:

public function actionParentLogin()
{
    $model = new LoginForm(['userModel' => ParentLogin::className()]);
    // calling model->login() here as we usually do
    if ($model->load(Yii::$app->request->post()) && $model->login()) {
            // no need to worry about checking if we found parent it's all done polymorphycally for us in LoginForm
            // here is the trick, since we loggin in via parentLogin action we set this session variable.
            Yii::$app->session->set('isParent', true);
            return $this->redirect(['parent-dashboard']);
        } else {
            Yii::$app->getSession()->setFlash('error', Yii::t('site', 'Incorrect username or password.'));
        }
    }
    return $this->render('parent-login', [
            'model' => $model,
        ]);
}

您的 parentLogin 模型应该扩展 User 模型以完成所有这些工作:

class parentLogin extends User
{
    public static function tableName()
    {
        //you parent users table name
        return 'parent_users';
    }

    public static function findByUsername($username)
    {
         return static::findOne(['p_username' => $username]);
    }
}

现在当您登录时,您需要处理身份切换,因为在配置中您有'identityClass' => 'app\models\User'。我们可以为此使用 bootstrap 属性:

//in your config file
'bootstrap' => [
    'log',
    //component for switching identities
    'app\components\IdentitySwitcher'
],

IdentitySwitcher class:

class IdentitySwitcher extends Component implements BootstrapInterface
{
    public function bootstrap($app)
    {
        //we set this in parentLogin action
        //so if we loggin in as a parent user it will be true
        if ($app->session->get('isParent')) {
            $app->user->identityClass = 'app\models\ParentLogin';
        }
    }
}