Validar campo de senha no JSF

Tenho um form de login:

[code]<h:form id=“login_form”>

	<h2><h:outputText value="Login" /></h2>
	<p id="primeiro">
	<h:outputText value="Login" />
	<h:inputText id="login" size="25" maxlength="15" 
					required="true" value="#{usuarioBean.login}" >
					<f:validator validatorId="validaLogin"/>
	</h:inputText>
	<h:message styleClass="erro" for="login" />
	</p>
	<p>
	<h:outputText value="Senha" />
	<h:inputSecret id="senha" size="25" value="#{usuarioBean.senha}" >
	<f:validator validatorId="validaSenha"/>
	</h:inputSecret>
	<h:message styleClass="erro" for="senha" />
	</p>
	<p>
	<h:commandButton action="#{usuarioBean.checaLogin}" styleClass="botao" id="enviar" 
					 value="Entrar" type="submit">
	</h:commandButton>
</h:form>[/code]

Criei um validador ‘validaLogin’ para ver se o login existe no banco de dados e queria usar o ‘validaSenha’ para testar se o login está correto, mas não estou conseguindo pegar os dois campos (login e senha) para checar o login, o meu método validade está assim:

[code]
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
String loginIn = (String)component.getAttributes().get(“login”);
UIInput loginInput = (UIInput) context.getViewRoot().findComponent(loginIn);
String login = (String) loginInput.getValue();
String senha = (String) value;
UsuarioDAO dao = new UsuarioDAO();
Map sessao = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();

	if (!dao.checaLogin(login, senha, sessao)) {
		FacesMessage message = new FacesMessage();
		message.setDetail("Senha  inválida!");
		message.setSummary("Login Invalido!");
		message.setSeverity(FacesMessage.SEVERITY_ERROR);
		throw new ValidatorException(message);
	}

}[/code]

Qual a forma certa de fazer isso?

Olá,

Você pode fazer o teste em um action e retornar a regra de navegação para a mesma página caso o login não seja satisfeito.
Sua solução não é ruim, mas vá de action que não tem erro.

Abraço

Na verdade os Validators do JSF ñ suportam Cross Field Validation, talvez no 2.1 :). De qualquer forma essa ñ parece mesmo a melhor maneira de implementar esse caso. Me parece mais coerente (e simples tb) q vc trate a autênticação como uma funcionalidade e use um ManagedBean p/ isso:

@Named
@RequestScoped
public class Authenticator {

  private String username;
  private String password;

  public String getUsername() { return username; }
  public void setUsername(String username) { this.username = username; }

  public String getPassword() { return null; } // Vc ñ vai querer expor a senha do usuário, vai?
  public void setPassword(String password) { this.password = password; }

  public void login() {
    // Aqui vc coloca a sua lógica de autênticação.
  }

}

Eu faria a seguinte alteração no código do dev.rafael:

String login(){}

Vlw pessoal! Funcionou beleza, eu coloquei assim:

public String checaLogin(){ UsuarioDAO dao = new UsuarioDAO(); Map sessao = FacesContext.getCurrentInstance().getExternalContext().getSessionMap(); if(dao.checaLogin(login, senha, sessao)){ return "sucesso"; } else if(!dao.existeUsuario(login, sessao)){ FacesMessage message = new FacesMessage("Login inválido"); message.setSeverity(FacesMessage.SEVERITY_ERROR); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Login inválido", null)); }else{ FacesMessage message = new FacesMessage("Senha inválido"); message.setSeverity(FacesMessage.SEVERITY_ERROR); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Senha inválida", null)); } return "falha"; }

E alterei na página para <h:messages/>, só queria saber o que teria que alterar para poder apresentar as mensagens individualmente, tipo <h:message for=“login” />

Desculpem. Erro meu.

Tranquilo, acontece.
Tenho acompanhado os foruns e você é uma pessoa que contribui em muito com respostas bem elaboradas.

Abraço

Em geral é um pouco melhor vc usar uma menssagem do tipo “Login ou senha inválida”, afinal vc ñ quer dar dicas à um possível hacker q está tentando hackear uma conta, ñ é?
Outra coisa, se vc estiver usando JPA, então o padrão DAO na sua aplicação é dispensável. Isso pq a class EntityManager do JPA te provê todos os benefícios de um DAO. Somente com o uso do EntityManager e a eliminação dos DAOs é q vc consegue uma arquitetura realmente MVC.

Não tinha pensado nisso, vlw!

Ah, e quanto ao DAO, eu estou usando porque estou fazendo este projeto para estudar, a princípio, somente o JSF, então estou armazenando o Banco de Dados em um Map na sessão mas quando eu começar a utilizar algum BD eu vou levar o JPA em conta :wink:

Obrigado pela ajuda e as dicas.

Só mais um detalhe. Use constantes p/ representar as suas regras de navegação. Um padrão q tem me ajudado muito é o seguinte:

public final class NavigationRule {

  public static final NavigationRule LOGIN_SUCCESS = new NavigationRule("success");
  public static final NavigationRule LOGIN_FAIL = new NavigationRule("fail");

  private String name;

  private NavigationRule(String name) { this.name = name; }

  public NavigationRule redirect() { return new NavigationRule(name + "?faces-redirect=true"); }

  @Override
  public String toString() { return name; }

}

Nos seus ManagedBeans vc, então pode:

import NavigationRules;
import static NavigationRules.*;

@Named
@RequestScoped
public class Authenticator {

  public NavigationRule login() {
    return LOGIN_SUCCESS.redirect();
  }

}

Interessante! Como funciona esse faces-redirect=true ?

Essa é uma funcionalidade nova no JSF 2, chamase implicit navigation. Até o JSF 1.2 vc precisava registrar todas as suas navigation rules no faces-config.xml. Já no JSF 2 basta q vc retorne o nome do arquivo p/ onde vc quer navegar sem a extenção (nome é caminho, é claro). P/ fazer redirecionamentos (q até o JSF 1.2 usavam ) vc só precisa adicionar o parametro faces-redirect=true à sua navigation-rule.