Ajuda na parte do login do projecto

Boas colegas.
Preciso de umas orientações vossas visto que não sou experiente, estou em volta de um projecto web, usando jsf, primefaces, apache tomcat e paginas em formato xhtml e conectado a uma base de dados mysql. O projecto esta andar bem, porém um dos requisitos seria permitir a autenticação de usuarios atraves do login, é ai que estou com dificuldade, tenho procurado pela internet mas sempre aparece exemplos mas a usarem hibernate e outras tecnologias, o que parece que não se enquadra com o meu projecto por ser algo simples. gostaria de saber o que tenho de fazer para que cada usuario possa ter acesso ao sistema, visto que construi perfis para cada usuario, (são tres) e controlo sessão.

Olá

Hibernate é para persistência de dados, ou seja, inserir os dados no banco.

Para controlar a sessão, vc pode ter uma classe Usuario e adicionar um atributo Usuario ao seu Bean SessionScoped ou adicionar o Usuario no contexto da aplicação através do ExternalContext.

Para autorização, você pode usar Phase Listener que é nativo do JSF ou então Spring Security.

Olá

Hibernate é para persistência de dados, ou seja, inserir os dados no banco.

Para controlar a sessão, vc pode ter uma classe Usuario e adicionar um atributo Usuario ao seu Bean SessionScoped ou adicionar o Usuario no contexto da aplicação através do ExternalContext.

Para autorização, você pode usar Phase Listener que é nativo do JSF ou então Spring Security.

Obrigado, como poderia implementar? Andei a pesquisar na internet e encontrei este tutorial http://benignosales.com.br/tutorial/criando-uma-pagina-de-login-completa-com-jsf-e-primefaces/, mas fiquei na duvida como poderia reaproveitar no meu projecto porque na classe bean do tutorial o autor usa @Named mas em todo o meu projecto usei sempre @ManagedBean, penso que teria de fazer alterações mas não sei realmente por onde começar… Deixarei as classes que já criei, talvez ajude um pouco

Classe de modelo de usuario

public class Usuario  {
	private int id;
	private String nome;
	private String login;
	private String senha;

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getNome() {
		return nome;
	}
	public void setNome(String nome) {
		this.nome = nome;
	}
	public void setLogin(String login) {
		this.login = login;
	}
	public String getLogin() {
		return login;
	}
	public String getSenha() {
		return senha;
	}

	public void setSenha(String senha) {
		this.senha = senha;
	}
}

Classe UsuarioDAO

public class UsuarioDAO {
	public void salvar(Usuario u) throws SQLException {
		StringBuilder sql = new StringBuilder();
		sql.append("Insert into usuario ");
		sql.append("(nome, login, senha) ");
		sql.append("values(?, ?, ? )");
		
		Connection conexao = Conexao.conectar();
		PreparedStatement stmt = conexao.prepareStatement(sql.toString());
		stmt.setString(1, u.getNome());
		stmt.setString(2, u.getLogin());
		stmt.setString(3, u.getSenha());
		stmt.executeUpdate();
					
	}
	
	public void excluir(Usuario u) throws SQLException{
		StringBuilder sql = new StringBuilder();
		sql.append("Delete from usuario ");
		sql.append("where idUsuario=?");
		
		Connection conexao = Conexao.conectar();
		PreparedStatement stmt = conexao.prepareStatement(sql.toString());
		stmt.setInt(1, u.getId());
		stmt.executeUpdate();
		
	}
	
	public void buscar(Usuario u) throws SQLException{
		StringBuilder sql = new StringBuilder();
		sql.append("select login, senha ");
		sql.append("from usuario ");
		sql.append("where login=? and senha=? ");
		
		Connection conexao = Conexao.conectar();
		PreparedStatement stmt = conexao.prepareStatement(sql.toString());
		stmt.setString(1, u.getLogin());
		stmt.setString(2, u.getSenha());
		stmt.executeUpdate();		
	}
}

Classe LoginBean

@ManagedBean(name = "MBLoginBean")
@SessionScoped
public class LoginBean implements Serializable {
	private Usuario usuario;

	public Usuario getUsuario() {
		return usuario;
	}
	public void setUsuario(Usuario usuario) {
		this.usuario = usuario;
	}	
}

Ou seja o meu problema acredito que esteja mesmo no tratamento da classe bean…

Você vai ter um formulário para fazer um login, certo?

Esse formulario vai conter os atributos da classe LoginBean, ou seja, o atributo usuario

Quando a sua página de login for visitada, caso o LoginBean não esteja na memoria, ele vai ser instanciado.

<p:outputLabel for="email" value="E-mail:"/>
<p:inputText  id="email" value="#{loginBean.usuario.email}"/>

<p:outputLabel for="senha" value="Senha:"/>
<p:password id="senha" value="#{loginBean.usuario.senha}"

<p:commandButton value="Entrar" action="#{loginBean.login()}"/>

Se você executar do jeito que a sua classe esta, vai gerar uma NullPointerException, pois vai ser tentando adicionar um valor ao atributo email e o seu objeto usuario vai estar nulo.

O seu login deve ter um método que faz a conexão ao banco.

Bom… Você tem um DAO que faz isso.

O jeito correto (no meu ponto de vista, quem sou eu né…) seria criar uma classe intermediária entre o Bean e o DAO, pois a classe DAO não pode ser referenciada no código (boas práticas).

Eu faria o seguinte:
Criaria um método que averiguasse se o usuário existe no banco, se existe, retorna ele então.

Criaria uma classe chamada UsuarioService, e teria um objeto UsuarioDAO. Criaria também um método que retorna Usuario, esse usuario viria de um método do UsuarioDAO.

E o bean teria um método que pega o retorno do método do UsuarioService, se o retorno for NULL, quer dizer que o usuario não esta cadastrado, se não for nulo, iguala o atributo usuario ao usuario retornado.

A anotação @ManagedBean esta ultrapassada ja, é recomendado usar o @Named do CDI (Estude que vale a pena, tem pouca coisa realmente necessária, então é rapidinho).

O CDI é bastante usado para Injeções de Dependências, através da anotação @Inject (A mais usada), você pode estar se pensando qual a vantagem? A vantagem é que é possível pegar o seu bean que está na mémoria, e coloca-lo como atributo de outro bean

@Named
@ViewScoped
public class CadastroProdutoBean {
    @Inject  
    private Usuario usuarioLogado;
}

Assim podendo acessar os atributos dele.

A outra forma de adicionar objetos na sessão, que é através de um map, confira abaixo:

FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("usuario", usuario);

Na linha acima eu adicionei o objeto usuário na sessão (que não necessariamente vai ser o objeto do seu LoginBean, na verdade você nem precisa adiciona-lo la).

Para recuperar o objeto da sessão, pode ser feito assim:

usuario = (Usuario) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("usuario");

Sim terá um formulário com campos para introduzir o nome de usuário e senha. Obrigado pela explicação Mike, mas na verdade fica um pouco difícil de entender de como aplicar relativamente a lógica e implementação não sou muito experiente para ser sincero. Será que podes ajudar a construir ficarei com uma ideia mais clara

Claro, eu te ajudo.

Se tiver dúvidas, só perguntar.

Primeiramente, eu colocaria o CDI no projeto.

No Tomcat tem que ter umas configurações extras para funcionar

Para ser sincero não fico muito seguro em começar em usar CDI, porque ainda nem sei bem como usar, provavelmente teria de alterar as outras classes bean para nao falar das configurações porque no meu projecto já esta conectado com a base de dados e a funcionar correctamente … certo??

Eu falei para usar CDI pq depois você só vai usar JSF com CDI.

Para fazer funcionar (depois arruma), cria uma instancia do seu DAO no bean.

Crie um método para fazer o login no bean.

No método de login, utilize o DAO para buscar o usuário no banco, e então atribua o retorno do método do DAO ao objeto usuario do bean.

Você também tem que alterar o seu método buscar. Se baseie no exemplo abaixo;

String sql = "select * from contatos where id =?";
    PreparedStatement stmt = this.con.prepareStatement(sql);
    stmt.setInt(1,id);
    ResultSet rs = stmt.executeQuery(); 
    if(rs.next){
        Contato contato = new Contato();  
        contato.setId(rs.getLong("id"));  
        contato.setNome(rs.getString("nome"));  
        contato.setEmail(rs.getString("email"));  
        contato.setEndereco(rs.getString("endereco"));  
        contato.setDataNascimento(data);
        return contato;
    }
    return null;

Fiquei sem entender um pormenor, ao alterar o método DAO buscar, como ira fazer a validação? Não terá que pegar a senha e usuário para lhe permitir entrar no perfil? A validação seria feita ao chamar o método que permitira tipo filtrar os campos que serviram para se autenticar? Ou seja o resultado do select

Ué, pra validar você vai tentar buscar o usuário no banco, você vai passar o e-mail e a senha na query.
Se a query trazer um registro, quer dizer que um usuário esta cadastro com aquele e-mail e senha
Se não trazer nenhum registro , então não tem ninguém cadastrado com aquele e-mail e senha, ou seja, o usuario vai ser nulo, se for nulo você exibe uma mensagem de erro.

No exemplo acima, foi usado o id como identificador, no seu caso você vai usar o e-mail e senha

A ideia seria fazer a autenticação com o nome de usuário e senha. Não sei se entendi mas então seria mais ou menos assim:

metodo DAO

public void ObterUsuario(Usuario u) throws SQLException{
StringBuilder sql = new StringBuilder();
sql.append("Select * from usuario ");
sql.append("where usuario=? and senha=? ");
Connection conexao = Conexao.conectar();
PreparedStatement stmt = conexao.prepareStatement(sql.toString());

ResultSet resultado = stmt.executeQuery();

if(resultado.next()){
Usuario u = new Usuario();
u.setUsuario(resultado.getString("usuario");
u.setSenha(resultado.getString("senha");
return u;

} else {
return null;  
}

}

Ai você tem que mudar o retorno do método, de void para Usuario

Ai no bean você faz assim:

@ManagedBean
@SessionScoped
public class UsuarioLogadoBean{
    ...
    public String login() {
        usuario = meuDAO.obterUsuario(usuario);
        
        if(usuario != null){
            return "index.xhtml";
        }
        else {
              FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "E-mail ou senha inválidos!", ""));
              return null;
        }
    }
}

Começo a entender, como eu tenho três perfis que são o admin (admin do sistema), administrador (quem controla) e funcionário (o controlado), devia encaminhar segundo uma condição pegando os valores dos campos e comparando resultaria se usa-se os metodos getters e setters do objecto? Tipo

    public String login() {
            usuario = meuDAO.obterUsuario(usuario);
            
            if(usuario.getUsuario.equals("Joao") and usuario.getSenha("1234")){
                return "indexUsuarioA.xhtml";

            } else if(usuario.getUsuario.equals("Alberto") and usuario.getSenha("4567")){
              return "indexUsuarioB.xhtml";

    }else if(usuario.getUsuario.equals("Fernanda") and usuario.getSenha("8910")){
 return "indexUsuarioB.xhtml";}
            else {
                  FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "E-mail ou senha inválidos!", ""));
                  return null;
            }
        }
    }

Seria algo mais ou menos assim:

if(usuario.isAdmin()) {
//Faz algo
}
else if(usuario.isAdministrador()){
//Faz algo
}
else if(usuario.isFuncionario()){
//Faz algo
}
//E assim por diante
Seria algo mais ou menos assim:

if(usuario.isAdmin()) {
//Faz algo
}
else if(usuario.isAdministrador()){
//Faz algo
}
else if(usuario.isControlado()){
//Faz algo
}
//E assim por diante

O que estaria a pegar com o usuario.isAdministrador e os restantes?

O que vc quiser fazer.

O sistema é seu, você que decide o que tem que acontecer caso o usuario for admin ou adminstrador ou funcionario

Por acaso o is pega algum valor?

Na realidade estou pouco confuso como resolver este problema porque, criei três paginas index ou página principal, um para funcionário que tem uma barra de ferramentas diferente com o index do administrador e admin, o mesmo se aplica aos outros dois usuários, isto porque cada usuário teria funções diferentes no sistema, teria que encaminha-los para as suas respectivas paginas principais. E como poderia fazer? Tentei da maneira que exemplificaste mas nao resultou assinalou erro e da minha tambem deu erro…

A classe DAO ficou assim

    public Usuario obterUsuario(Usuario u) throws SQLException {
		StringBuilder sql = new StringBuilder();
		sql.append("Select * from usuario ");
		sql.append("where usuario=? and senha=? ");

		Connection conexao = Conexao.conectar();
		PreparedStatement stmt = conexao.prepareStatement(sql.toString());

		ResultSet resultado = stmt.executeQuery();

		if (resultado.next()) {
			u.setUsuario(resultado.getString("usuario"));
			u.setSenha(resultado.getString("senha"));
			return u;
		} else {
			return null;
		}

	}

e a classe BEAN

    public void login() {
		
		try {
			UsuarioDAO udao = new UsuarioDAO();
			usuario = udao.obterUsuario(usuario);
		
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

O is seria o get.

Quando se tem uma variável do tipo boolean, a convenção diz para usar is no lugar de get.

Qual seria o erro?

Tem a maneira correta, que é usar um autorizador para ser executado em todas as páginas que for acessar e tem a maneira para teste.

Vamos pro teste inicialmente:
Se você quer fazer um redirecionamente para uma página depois de efetuar um login, você tem que mudar o retorno do seu método para String, e então você retorna o nome da página ou pasta + página juntamente com a extensão

return “index.xhtml”;