Login com filter - JSF e Hibernate

Bom dia a todos!

Estou tentando implementar a parte de login com filter, mas estou esbarrado faz tempo já num problema.
Estou usando JSF 2.1 e Hibernate 3

Se alguém puder dar uma luz ai por favor

Mensagem de Erro:

GRAVE: 'org.hibernate.TransactionException' recebido ao invocar escuta de ação '#{loginBean.login}' para o componente 'loginButton'
GRAVE: org.hibernate.TransactionException: Transaction not successfully started
	at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:100)
	at com.changes.dao.impl.UsuarioDaoImpl.isValidLoginAndPassword(UsuarioDaoImpl.java:40) //O primeiro ele mostra aqui
	at com.changes.bean.LoginBean.login(LoginBean.java:36)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.sun.el.parser.AstValue.invoke(AstValue.java:254)
	at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:302)
	at javax.faces.event.MethodExpressionActionListener.processAction(MethodExpressionActionListener.java:153)
	at javax.faces.event.ActionEvent.processListener(ActionEvent.java:88)
	at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:769)
	at javax.faces.component.UICommand.broadcast(UICommand.java:300)
	at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
	at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
	at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
	at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
	at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
	at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
	at com.changes.util.AuthFilter.doFilter(AuthFilter.java:59)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)

UsuarioDaoImpl

@Override
    public boolean isValidLoginAndPassword(String login,String password) {
        boolean valid;
        Session session = HibernateUtil.getSessionFactory().openSession();
        String sql = "select u from Usuario u where username=:user and password=:pass";
        Query query = session.createQuery(sql);
        query.setString("user", login);
        query.setString("pass", password);
        Usuario usr = (Usuario) query.uniqueResult();
        valid = (usr != null);
        session.getTransaction().commit(); //o erro é apontado aqui (já depurei e aqui não aparece nenhum valor...)
        session.close();
        return valid;
    }

LoginBean

@ManagedBean(name="loginBean")
@SessionScoped
public class LoginBean {
    
    private Usuario usuario;
    private String user;
    private String password;


    public LoginBean() {
        
        /*if (this.usuario == null) {
            this.usuario = new Usuario();
        }*/
    }
    
    
    public String login() {
        UsuarioDao usuarioDao = new UsuarioDaoImpl();
        if (usuarioDao.isValidLoginAndPassword(user, password)) {
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("user", "ok");
            return "index.jsf";
        } else {
            return "reloadError";
        }
    }

    public Usuario getUsuario() {
        if (usuario == null){
            usuario = new Usuario();
        }
        return usuario;
    }

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

login.xhtml

<p:growl id="growl" showDetail="true" life="3000" />
        <p:dialog id="dialog" header="Login" visible="true" closable="false" draggable="false" resizable="false" width="420" widgetVar="dlg">
            <h:form>
                <h:panelGrid columns="2" cellpadding="5">
                    <h:outputLabel for="username" value="Username: " /> 
                    <h:inputText value="#{loginBean.usuario.username}"
                                 id="username" required="true" label="username"/>
                    <h:outputLabel for="password" value="Password: " />
                    <h:inputSecret value="#{loginBean.usuario.password}" 
                                   id="password" required="true" label="password"/>
                    <f:facet name="footer">
                        <p:commandButton id="loginButton" value="Login" update=":growl" 
                                         actionListener="#{loginBean.login}" 
                                         oncomplete="handleLoginRequest(xhr, status, args)"/>
                    </f:facet>
                </h:panelGrid>
            </h:form>
        </p:dialog>

Se realmente for ali onde apontei o problema, não estou conseguindo achar o jeito de aparecer um valor valido

Valeu

Bom dia rafa,

O problema acontece porque sua transação não foi iniciada: Transaction not successfully started.
Você abre sessão do hibernate e depois lá onde da o erro vc pede a transição: session.getTransaction().commit();
tente iniciar a transição logo depois de abrir a sessão:

Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction();

Boa, passou… porém não está pegando o usuario e a senha.

Quando executo e digito o login e senha ele passa pelo select e para. E no deputador não vem valor nenhum.

Como posso fazer para pegar o valor digitado?

 select
        usuario0_.id as id1_,
        usuario0_.perfil_id as perfil2_1_,
        usuario0_.name as name1_,
        usuario0_.email as email1_,
        usuario0_.username as username1_,
        usuario0_.password as password1_ 
    from
        changescontrol.usuario usuario0_ 
    where
        usuario0_.username=? 
        and usuario0_.password=?

UsuarioDaoImpl

public class UsuarioDaoImpl implements UsuarioDao {

    @Override
    public boolean isValidLoginAndPassword(String username,String password) {
        boolean valid;
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        Query query = session.createQuery("from Usuario usr where usr.username=:user and usr.password=:pass");
        query.setString("user", username);
        query.setString("pass", password);
        Usuario usr = (Usuario) query.uniqueResult();
        valid = (usr != null);
        session.getTransaction().commit();
        session.close();
        return valid;
    }

}

LoginBean

@ManagedBean(name="loginBean")
@SessionScoped
public class LoginBean {
    
    private Usuario usuario;
    private String user;
    private String password;


    public LoginBean() {
        
        /*if (this.usuario == null) {
            this.usuario = new Usuario();
        }*/
    }
    
    
    public String login() {
        UsuarioDao usuarioDao = new UsuarioDaoImpl();
        if (usuarioDao.isValidLoginAndPassword(user, password)) {
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("user", "ok");
            return "index.jsf";
        } else {
            return "reloadError";
        }
    }

    public Usuario getUsuario() {
        if (usuario == null){
            usuario = new Usuario();
        }
        return usuario;
    }

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

Essa SQL que mostra no console está certa, porém você não esta associando os inputText da página com as variáveis que você está usando no MB,

veja que você pede para o inputText ir para o campo username do usuário:

<h:inputText value="#{loginBean.usuario.username}" id="username" required="true" label="username"/>
só que no LoginBean você não inicializa a variável usuario e o pior, você usa as variáveis user e password para tentar validar:

usuarioDao.isValidLoginAndPassword(user, password)

user e password está nulo, então a SQL não vai encontrar o que quer.

Você tem duas alternativas: inicializa o usuario no construtor e manda o usuario.username e usuario.password no login:

usuarioDao.isValidLoginAndPassword(usuario.getUsername(), usuario.getPassword())

ou exclui esse usuario que você não está usando e muda os inputText para as variáveis user e password:

<h:inputText value="#{loginBean.user}" id="username" required="true" label="username"/>

<h:inputSecret value="#{loginBean.password}" id="password" required="true" label="password"/>

Eu nunca usei createQuery, normalmente uso o Criteria do Hibernate, mas pela sql que está mostrando o DAO está ok, o erro é o que citei acima.

Eu alterei o método no dao e no bean para funcionar.

Eu consigo logar com usuario e senha q digito, mas o filter não está funcionando. Eu consigo ir direto para página index.jsf sendo que nao era pra deixar
Ou até mesmo se eu logo e depois volto para pagina de login e volto para index de novo, ele aceita … ou seja…nao está filtrando

Alguma ajuda?

loginBean

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

    public LoginBean() {
        
        if (this.usuario == null) {
            this.usuario = new Usuario();
        }
    }
        
    public void redirecionar() throws IOException{    
       FacesContext faces = FacesContext.getCurrentInstance();
        ExternalContext context = faces.getExternalContext();
        context.redirect("/ControleChanges/index.jsf");   
    }  
    
    public void login(ActionEvent actionEvent) throws IOException {
        RequestContext context = RequestContext.getCurrentInstance();
        FacesMessage msg = null;
        boolean loggedIn = false;
        
        UsuarioDao usuarioDao = new UsuarioDaoImpl();
        usuario = usuarioDao.buscarPorUsuario(usuario);

        if (usuario != null) {
            loggedIn = true;
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("user", "ok");
            redirecionar();
            msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Welcome", usuario.getUsername()); //"Usuario"
        } else {
            loggedIn = false;
            msg = new FacesMessage(FacesMessage.SEVERITY_WARN, "Login Error", "Invalid credentials");
        }

        FacesContext.getCurrentInstance().addMessage(null, msg);
        context.addCallbackParam("loggedIn", loggedIn);
    }

    public Usuario getUsuario() {
        if (usuario == null){
            usuario = new Usuario();
        }
        return usuario;
    }

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

UsuarioDaoImpl

public class UsuarioDaoImpl implements UsuarioDao {
   
    @Override
    public Usuario buscarPorUsuario(Usuario usuario) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        String sql = "select u from Usuario u where username=:user and password=:pass";
        Query query = session.createQuery(sql);
        query.setString("user", usuario.getUsername());
        query.setString("pass", usuario.getPassword());
        return (Usuario) query.uniqueResult();
    }
}

AuthFilter

public class AuthFilter implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest rq = (HttpServletRequest) request;
        HttpServletResponse rp = (HttpServletResponse) response;
        boolean auth = rq.getSession().getAttribute("user") != null;

        if (!auth && !rq.getRequestURL().toString().contains("login.jsf")) {
            rp.sendRedirect(rq.getContextPath() + "/login.jsf");
        } else {

            try {
                chain.doFilter(request, response);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void destroy() {
    }

    public void init(FilterConfig config) throws ServletException {
    }
}

Você configurou o web.xml para fazer a filtragem das páginas?

Essa configuração de impedir que acesse tais páginas fica no web.xml, onde você informa que todas páginas da pasta “x” só podem ser acessadas se logadas, e as páginas da raiz ou de oEutras pastas estão livres

Normalmente eu configuro para as páginas que estão na raiz do webcontent para serem acessíveis sem login, coloco então na raiz o index e o login, mas todas as outras páginas fica dentro de /pages, e defino que o usuário tem que logar para acessá-las, veja exemplo dessa configuração:

<filter>
  	<filter-name>Filtro Login</filter-name>
  	<filter-class>package.AuthFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>Filtro Login</filter-name>
    <url-pattern>/pages/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
  </filter-mapping>

fiz o web.xml sim, esqueci de postar
inclusive ele retira todo meu css da pagina de login :?

Porém não está como o seu, vou testar o seu e já falo

O meu web.xml

    <filter>
        <display-name>AuthFilter</display-name>
        <filter-name>AuthFilter</filter-name>
        <filter-class>com.changes.util.AuthFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

adicionei somente a parte de do web.xml e mesmo assim logo, mas o filtro ainda nao funciona

ja nem sei mais oq fazer

Só uma dica, crie uma classe que represente um usuário não logado ao invés de deixar como null, isso pode gerar algumas confusões como no retorno de getAttribute de HttpSession:

Aqui você passa a ter duas possibilidades de um retorno null, o objeto sendo null ou a não for encontrado um objeto com esse nome, a ainda o problema de depender da implementação interna de HttpSession que pode criar uma cópia do objeto ou copiar a instância em si, se HttpSession cria e guarda uma cópia do objeto uma instância null deixa de ser tratada como tal.

Pode ou não ser o caso, por conta da implementação interna, um exemplo para facilitar o entendimento do que estou dizendo:

[code]static class Car {
public String name;
public Car(String name) {
this.name = name;
}
public Car(Car other) {
if (other != null)
this.name = other.name;
}
}

public static void main(String[] args) {
    Car c = null;
    
    Car c2 = c; //cópia da instância
    Car c3 = new Car(c); //cópida do objeto
    
    if (c2 != null)
        System.out.print("not ");
    System.out.println("is null");

    if (c3 != null)
        System.out.print("not ");
    System.out.println("is null");
    
}[/code]

a saída é:

is null not is null

[quote=rafa120]adicionei somente a parte de do web.xml e mesmo assim logo, mas o filtro ainda nao funciona

ja nem sei mais oq fazer[/quote]

Como seu problema é que a aplicação aceitar acessar as páginas que não era para deixar, analisei apenas o AuthFilter e o web.xml.
Primeiramente, com o url-pattern /* você exclue o CSS e ele não funcionará mesmo, por isso eu criei a pasta /pages para colocar as páginas da aplicação, deixando livre do filtre o CSS, página de login, imagens, etc.

O seu AuthFilter está correto, segue a mesma lógica do que tenho aqui em 5 aplicações funcionando perfeitamente.
Agora seu web.xml esta diferente no url-pattern que te falei, já apanhei muito tentando colocar todo webContent no filtro e sempre dava algum bug, depois que fiz a pasta pages para armazenar as páginas que necessitam de loguim os bugs foram embora…
Caso não queira criar a pasta “pages”, liste todas as páginas que você quer bloqueiar no url-pattern.