成员资格提供商ChangePassword方法返回类型问题

时间:2009-12-29 22:18:49

标签: asp.net-membership membership-provider asp.net-webcontrol

我们已经实施了自定义成员资格提供程序,并在使用此提供程序的网页上进行了更改密码控制。此成员资格提供程序类中的ChangePassword方法通过连接到外部Web服务来检查有关密码强度和有效性的一些业务逻辑。 web服务能够准确地返回新密码的错误(如果有的话)(长度问题,需要特殊字符等)。

现在,必须由自定义提供程序覆盖的ChangePassword方法的签名是:

public override bool ChangePassword(string username, string oldPassword, string newPassword)

因此即使我知道用户提供的新密码的确切问题,我也无法在网页上显示它,因为我只能从此方法返回true或false,然后更改密码控件接管并且根据布尔返回值做自己的魔法。我可以连接OnChangePasswordError控件的ChangePassword事件以显示静态错误消息,或者我甚至可以在出现错误时将此控件的FailureText属性设置为某些硬编码字符串发生了,但我无法向用户提供他们提供的密码到底出了什么问题。

protected void OnPasswordChangeError(object sender, EventArgs e)
        {
            throw new MembershipPasswordException("Tell user what exactly is wrong with the password they supplied");
        }

MembershipProvider类有一个ValidatingPassword事件,在密码更改之前引发,我可以通过检查密码是否符合条件来抛出异常,但是这个异常仍然没有似乎被传递给ChangePassword控件。以下是ValidatingPassword eventHandler的代码:

void MyMembershipProvider_ValidatingPassword(object sender, ValidatePasswordEventArgs e)
        {
           //if password not valid
           e.Cancel = true;
           e.FailureInformation = new MembershipPasswordException();
           e.FailureInformation;           
        }  

如何将Membership provider类的ChangePassword方法中的特定信息发送到ChangePassword控件,以向用户显示正确的,非静态/硬编码的密码更改错误消息?有没有办法将ValidatePasswordEventArgs连接到OnChangePassword方法的EventHandler中的EventArgs,以便我可以在ChangePassword控件中获取FailureInformation?

从我最初的研究来看,这似乎是不可能的。虽然我觉得MS团队不会忽略这一点,但应该有办法。

一些指示:

MembershipUser.ChangePassword fails without warning http://forums.asp.net/t/983613.aspx

4 个答案:

答案 0 :(得分:2)

好的,所以我终于想出了一个解决方法。它不是最干净的,但是我唯一可以提出或发现的任何一个与默认情况下应该包含的内容相近的地方。

注意:我没有选择在数据库或cookie中保留它。

我创建了一个ChangePasswordNew方法,其行为与MembershipProvider ChangePassword方法相同,但返回字符串而不是bool。所以我的新方法看起来像这样:

public string ChangePasswordNew(string username, string oldPassword, string newPassword)
{
    //if password rules met, change password but do not return bool as MembershipProvider method does, 
    //return Success or the exact error for failure instead
    if(passwordChangeRequirementsMet == true)  
    {
        //change the password
        return "success";
    }
    else          
        return "exact reason why password cannot be changed";
}

现在,订阅onPasswordChanging控件的ChangePassword事件:

protected void PasswordIsChanging(object sender, LoginCancelEventArgs e)
{
try
    {
      string response = Provider.ChangePasswordNew(username, currentPass, newPass);
      if(response != "success")
      {
        changePwdCtrl.FailureText = response;
      }
     else
    {
    //cancel call to the default membership provider method that will attempt to
    //change the password again. Instead, replicate the 'steps' of AttemptChangePassword(), 
    //an internal method of the ChangePassword control.
    //Performing all the steps instead of calling the method because just calling method
    //does not work for some reason

    e.Cancel = true;
    FormsAuthentication.SetAuthCookie(username, false);
    OnPasswordChanged(sender, e);

    MethodInfo successMethodInfo = changePwdCtrl.GetType().GetMethod("PerformSuccessAction",                                    BindingFlags.NonPublic | BindingFlags.Instance);
    successMethodInfo.Invoke(changePwdCtrl, new object[] { "", "", changePwdCtrl.NewPassword });
}
}
catch(Exception ex)
{
    LogException(ex);
    throw;
}

}

注意:在这种情况下,如果密码更改出错,即响应不是“成功”,则仍会调用Memebership Provider ChangePassword方法并返回再次出错。但我已经将FailureText属性设置为第一次调用中从响应返回的描述性错误,这是我的目标。在我的情况下,我不介意两个方法调用。你的情况可能不同。

希望这有助于某人!

答案 1 :(得分:2)

您可以这样做,这样您就不会两次调用changepassword方法。

if(response != "success") 
      { 
       e.Cancel = true;
((Literal)changePwdCtrl.ChangePasswordTemplateContainer.FindControl("FailureText")).Text = response;
      }

我创建了一个自定义membershipprovider类来处理这个问题,在成员类类中抛出changepassword方法抛出特定消息的错误消息,然后将其返回到更改密码控件的控件文字。

protected void ChangePassword1_ChangingPassword(object sender, LoginCancelEventArgs e)
    {
        try
        {
            ChangePassword c = (ChangePassword)sender;

            MembershipUser mu = Membership.GetUser(c.UserName);

            bool response;

            try
            {
                response = mu.ChangePassword(c.CurrentPassword, c.NewPassword);
            }
            catch (Exception ex)
            {

                response = false;
                e.Cancel = true;
                //ChangePassword1.ChangePasswordFailureText = ex.Message;
                //ChangePassword1_ChangePasswordError(sender, e);      
                //((Literal)ChangePassword1.ChangePasswordTemplateContainer.FindControl("FailureText")).Visible = true;
                ((Literal)ChangePassword1.ChangePasswordTemplateContainer.FindControl("FailureText")).Text = ex.Message;

            }

            if (response)
            {        
                //cancel call to the default membership provider method that will attempt to 
                //change the password again. Instead, replicate the 'steps' of AttemptChangePassword(),  
                //an internal method of the ChangePassword control. 
                //Performing all the steps instead of calling the method because just calling method 
                //does not work for some reason 

                e.Cancel = true;
                FormsAuthentication.SetAuthCookie(c.UserName, false);
                ChangePassword1_ChangedPassword(sender, e);
                MethodInfo successMethodInfo = ChangePassword1.GetType().GetMethod("PerformSuccessAction", BindingFlags.NonPublic | BindingFlags.Instance);
                successMethodInfo.Invoke(ChangePassword1, new object[] { "", "", ChangePassword1.NewPassword });

            }
        }
        catch (Exception ex)
        {            
            throw;
        } 

答案 2 :(得分:1)

有一段时间我需要从提供商处传递信息,最后只需设置一个cookie,然后再返回你的bool,以便在通话的另一端接听。工作得很好。

答案 3 :(得分:0)

我相信更简洁的解决方法是让ChangePassword方法抛出异常。该异常包含错误消息!