Pergunta

Implementamos um provedor de associação personalizado e temos um controle de senha de alteração em uma página da Web que usa esse provedor. O método ChangePassword nesta classe de provedor de membros verifica alguma lógica de negócios sobre a força e a validade da senha conectando -se a um serviço da web externo. O WebService tem a capacidade de retornar exatamente o que há de errado com a nova senha, se houver (problemas de comprimento, caracteres especiais necessários etc.).

Agora, a assinatura do método ChangePassword que deve ser substituído por um provedor personalizado é:

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

Portanto, embora eu saiba o problema exato com a nova senha que o usuário fornece, não consigo exibi -lo na página da Web, porque só posso retornar um verdadeiro ou falso desse método e o controle de senha de alteração assume o controle e faz seu próprio Magic, dependendo do valor de retorno booleano. Eu posso conectar o OnChangePasswordError evento do ChangePassword controle para mostrar uma mensagem de erro estática, ou eu posso até configurar o FailureText Propriedade desse controle para alguma string codificada quando ocorre um erro, mas não consigo fornecer ao usuário o que exatamente está errado com a senha que eles forneciam.

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

o MembershipProvider A classe tem um ValidatingPassword Evento, que é aumentado antes que a senha seja alterada, e eu posso fazer uma exceção aqui, verificando se a senha atender aos critérios, mas ainda assim essa exceção não parece ser passada para o controle do ChangePassword. Aqui está o código para o ValidatingPassword EventHandler:

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

Como enviar informações específicas do método ChangePassword da classe de provedor de associação para o controle de alterações e exibir as mensagens de erro de alteração de senha corretas, não estáticas/codificadas para o usuário? Existe uma maneira de conectar o validepasswordEventargs aos eventos no EventHandler para o método OnChngePassword para que eu possa obter a informação de falha no controle do ChangePassword?

Da minha pesquisa inicial, isso não parece ser possível. Embora eu sinta que a equipe do MS não teria esquecido isso e deveria haver uma maneira.

Algumas dicas:

Associados.changepassword falha sem aviso http://forums.asp.net/t/983613.aspx

Foi útil?

Solução

Ok, então finalmente imaginei uma solução alternativa. Não é o mais limpo, mas o único que eu poderia encontrar ou encontrado em qualquer lugar que estivesse próximo do que deveria ter sido incluído por padrão.

Nota: Eu não tinha a opção de persistir isso em um banco de dados ou um cookie.

Eu criei um ChangePasswordNew método que tem o mesmo comportamento que o MembershipProvider ChangePassword método, mas retorna a string em vez de bool. Então, meu novo método seria assim:

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";
}

Agora, assine o onPasswordChanging evento do ChangePassword Ao controle:

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;
}

}

Observação: Nesse caso, se houver um erro na alteração de senha, ou seja, a resposta não é "sucesso", o provedor de memebership ChangePassword O método ainda será chamado e retornará um erro novamente. Mas já estou definindo o FailureText Propriedade para o erro descritivo retornado da resposta na primeira chamada, que foi o meu objetivo. Não me importei com duas chamadas de método no meu caso. Seu caso pode ser diferente.

Espero que isso ajude alguém!

Outras dicas

Em vez disso, você pode fazer isso, assim, não está chamando o método ChangePassword duas vezes.

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

Criei uma classe personalizada de associações que lida com isso, o método ChangePassword na classe de associação e a mensagem de erro da mensagem específica e devolvo isso ao controle literal do controle de senha de alteração.

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;
        } 

Ao mesmo tempo, eu precisava transmitir informações de um provedor e acabei definindo um cookie antes de devolver seu bool para ser recolhido no outro extremo da chamada. Funcionou bem.

Acredito que uma solução alternativa mais limpa é para o método de mudança de vasilha para lançar uma exceção. A exceção contém a mensagem de erro!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top