Solucionado (ver solução)
Solucionado
(ver solução)
20
respostas

Erro ao fazer login

Boa tarde,

estou com erro ao fazer login no meu sistema, recebo o erro:

Reason: Bad credentials

meu select gerado pelo Hibernate:

Hibernate: select usuario0_.id as id1_6_, usuario0_.email as email2_6_, usuario0_.emailAlternativo as emailAlt3_6_, usuario0_.nome as nome4_6_, usuario0_.roles as roles5_6_, usuario0_.senha as senha6_6_, usuario0_.telefone as telefone7_6_ from Usuario usuario0_ where usuario0_.email=?

meu metodo no DAO:

@Override
    public UserDetails loadUserByUsername(String email) {
        List<Usuario> usuarios = manager.createQuery("select u from Usuario u where u.email = :email", Usuario.class)
                .setParameter("email", "'" + email + "'").getResultList();

        if (usuarios.isEmpty()) {
            throw new UsernameNotFoundException("O usuário " + email + " não foi encontrado");
        }

        return usuarios.get(0);
    }

meu form de login:

<form:form servletRelativeAction="/login" method="post">
                        <h2>Email</h2>
                        <div class="email">
                            <span class="icon-user"></span>                    
                            <label><input class="nome-email" type="text" name="username" placeholder="  email@email.com" autofocus required="Insira seu Email"></label>
                        </div>
                        <h2>Senha</h2>
                        <div class="email">
                            <span class="icon-key3"></span>                    
                            <label><input class="nome-email" type="password" name="password" placeholder="  *******" autofocus required="Insira seu Email"></label>
                        </div>
                        <div class="login-botao">

                            <button class="botao-login" type="submit">Entrar</button>
                            <input type="hidden" name="${_csrf.parameterName }" value="${_csrf.token }" />
                        </div>    
                    </form:form>

obrigado.

20 respostas

Voce colocar o stack trace mais completo para que possamos analisar?

Fala Gabriel, tudo bem ?

Tente mudar o valor do parâmetro que você passa na query. Não precisa concatenar a String, a jpql gerencia essa necessidade. Talvel dessa maneira ele esteja buscando por um email com o seguinte valor 'email@email.com' - com aspas.

manager.createQuery("select u from Usuario u where u.email = :email", Usuario.class)
        .setParameter("email", email)
        .getResultList();

Veja se isso já muda o comportamento.

Abraço!

Rafael não sai erro algum no console esse erro aparece na tela de login do Spring.

Tudo bom Rafael Rollo e vc?

quando eu tiro as concatenações ele me volta esse erro:

failed to lazily initialize a collection of role: br.com.rprvidros.models.Usuario.pedido, could not initialize proxy - no Session

acredito que seja pela alteração que fiz com o Rafael Augusto, que me ajudou na outra duvida, eu coloquei as Roles como uma coluna na minha tabela usuario e nao mais uma tabela a parte.

Fala Gabriel,

Como estão suas classes Usuario e Role ? Coloque seus modelos aqui pra gente dar uma olhada.

classe Usuario:

package br.com.rprvidros.models;

import java.util.Collection;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

@Entity
public class Usuario implements UserDetails {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String nome;
    private String senha;
    private String email;
    private String emailAlternativo;
    private String telefone;
    private String roles ;
    @OneToMany(mappedBy = "id", targetEntity = Pedido.class)
    private List<Pedido> pedido;

    public List<Pedido> getPedido() {
        return pedido;
    }

    public void setPedido(List<Pedido> pedido) {
        this.pedido = pedido;
    }

    public static long getSerialversionuid() {
        return serialVersionUID;
    }

    public Integer getId() {
        return id;
    }

    public String getNome() {
        return nome;
    }

    @Override
    public String toString() {
        return "Usuario [id=" + id + ", nome=" + nome + ", senha=" + senha + ", email=" + email + ", emailAlternativo="
                + emailAlternativo + ", telefone=" + telefone + ", pedido=" + pedido + "]";
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getSenha() {
        return senha;
    }

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

    public String getTelefone() {
        return telefone;
    }

    public void setTelefone(String telefone) {
        this.telefone = telefone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getEmailAlternativo() {
        return emailAlternativo;
    }

    public void setEmailAlternativo(String emailAleternativo) {
        this.emailAlternativo = emailAleternativo;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return senha;
    }

    @Override
    public String getUsername() {
        return email;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRoles() {
        return roles;
    }

    public void setRoles(String roles) {
        this.roles = roles;
    }




}

classe role:

package br.com.rprvidros.models;

import javax.persistence.Entity;
import javax.persistence.Id;

import org.springframework.security.core.GrantedAuthority;

@Entity
public class Role implements GrantedAuthority {

    private static final long serialVersionUID = 1L;

    @Id
    private String nome;

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    @Override
    public String getAuthority() {
        return this.nome;
    }

}

estão diferentes da do curso a minha role no usuario é apenas uma string

Boa Gabriel,

Na verdade não faz sentido esse problema (lazy initialization) ser gerado pela query do login. Talvez em algum outro ponto do código você esteja consumindo essa lista (pedido) e aí quando ele tenta ir buscar os dados (tradicionalmente quando tentamos consumir numa página), estoura essa exception.

Pra termos certeza, coloca aqui toda a stack trace dessa exception.

Dica: tente já garantir uma referência pras suas collections e usar nome mais semantico dando o sentido de colecao de dados (pedidos)

@OneToMany(mappedBy = "id", targetEntity = Pedido.class)
private List<Pedido> pedidos = new ArrayList<>();

Abraço!

segue o stack trace completo, ele fala na linha 56 da classe usuario isso esta no meu metodo toString:

@Override
    public String toString() {
        return "Usuario [id=" + id + ", nome=" + nome + ", senha=" + senha + ", email=" + email + ", emailAlternativo="
                + emailAlternativo + ", telefone=" + telefone + ", pedido=" + pedidos + "]";
    }

acredito que nao esteja consumindo essa lista em lugar algum.

Quanto a dica ja corrigi obrigado.

Fala Gabriel,

Não veio o código da stack trace (o log completo que aparece no seu console quando alguma exception não é tratada e para seu sistema). Mas mesmo assim o problema deve ser aí sim.

Em algum local após o login você deve estar chamando o toString do usuário (feito também quando imprimimos direto a referência de Usuário - System.out.println(usuario)). Como sua consulta so traz o conteudo do Usuario (lazy initialization) sem o conteudo da lista, o toString tenta imprimir a lista ali, e quando é feito isso o hibernate tenta buscar o conteudo da lista, mas como a própria exception diz, não há mais sessão com a base de dados.

Tente identificar onde você chama efetivamente o método toString (possivelmente uma linha abaixo do seu log que mostra 56 da classe Usuário). Aí você pode pensar em remover a linha, e futuramente planejar a busca do conteudo dos pedidos do usuário.

Puts nem percebi desculpa cara, verdade eu tirei e "funcionou", veja se vc pode me ajudar com uma ultima questão, ao clicar em um botão "Finaliza compra", eu estou fazendo uma gambiarra acredito eu, porque eu preciso verificar se há um usuario para fechar o pedido, eu recebo esse metodo nesse controller:

@RequestMapping("/finaliza")
    public ModelAndView finalizaPedido(Usuario usuario){

        if (usuario.getSenha() == null){
            return new ModelAndView("/login");
        }

         ModelAndView modelAndView = new ModelAndView("rprvidros");
         modelAndView.addObject("usuario", usuario);
         return modelAndView;
    }

nele eu faço um if para ver se há um usuario, claro q no primeiro momento não vai haver e ele vai cair nesse if, só que acredito que eu não esteja guardando esse usuariona session, não acredito, tenho certeza, porque se refaço o processo de compra após fazer o login, ele cai no mesmo if ou seja o usuário continua null, como posso pegar o usuário após eu enviar a request no "finalizar pedido"?

muito obrigado.

solução!

Fala Gabriel,

Como você está usando Spring Security você pode delegar pra ele esse tipo de responsabilidade (so permitir executar a action com um usuário logado).

Você pode colocar na classe de configurações de segurança essa verificação:

@Override
protected void configure(HttpSecurity http) throws Exception {        
    http.authorizeRequests()
        ...
        .antMatchers("/finaliza").authenticated()
        ...
    }

Ou simplesmente omitir o matcher específico pra finaliza, mas indicar que todo request não previsto seja autenticado.

@Override
protected void configure(HttpSecurity http) throws Exception {        
    http.authorizeRequests()
        ...
        .antMatchers("/outraRequisicao").permitAll()
        .antMatchers("/maisUma").hasRole("ADMIN")
        .anyRequest().authenticated() // qualquer outra requisição diferente das citadas acima só serão atentidadas caso haja um usuário logado na sessão.
        ...
    }

PS: Essa solução é mostrada no curso.

Quando o Spring Security perceber que você está acessando essa url ele vai verificar se há usuário logado. Se houver, executa a ação, senão, redireciona pra página de login, e após logar executa a ação.

Aí na sua action quando precisar pegar o usuário pra qualquer que seja a operação basta pedir pro container do Spring injetar ele no método pra você através de @AuthenticationPrincipal

@RequestMapping("/finaliza")
public ModelAndView finalizaPedido( @AuthenticationPrincipal Usuario usuario){

    // não precisa mais do if que verifica se usuário é nulo e redireciona pra login
    // Spring Security já se apropria disso e aqui temos certeza agora que existe usuário logado.

    String email = usuario.getEmail();
    //pega dados 
    Pagamento pagamento = new Pagamento();
    //fecha pagamento

    //redireciona pra view de confirmação de pagamento
    }

Aí o fluxo fica muito mais simples.

Não precisa nem adicionar usuário no ModelAndView. O SpringSecurity mantém o usuário na sessão e dessa forma podemos acessar o mesmo na jsp.

Basta usar a taglib do spring security e acessar o objeto:

<security:authorize access="isAuthenticated()">
    <security:authentication property="principal" var="usuario">

    Bem-vindo, ${usuario.nome}
</security:authorize>

Espero ter ajudado.

Abraço!

Rafael cara muito obrigado mesmo, só uma coisa que quando eu faço o login ele volta para a pagina inicial, resolvi colocando essa ultima linha:

.and().formLogin().loginPage("/login").defaultSuccessUrl("/carrinho/finaliza")

muito obrigado a resposta era bem óbvia.

abraços

Fala Gabriel, tudo bem ?

Boa! Só toma um cuidado, que com essa linha estamos dizendo que depois de fazer login todo usuário será redirecionado pra um fechamento de compra. E nos casos onde o usuário fez login na app, mas ainda não está comprando. Em geral na defaultSuccessUrl(..) colocamos uma URL de algo que seja mais como uma home page.

E deixamos o comportamento de interceptar a requisição e logar antes do usuário realizar o fechamento pro security gerenciar. Se o usuário acessar /fechamento e nao estiver logado o security sabe redirecionar pro form de login e depois do login, ai sim ir direto pro ultimo caminho acessado antes da interceptacao, ou seja, o /fechamento. Esse comportamento é padrão do framework.

Tudo bom e vc Rafael,

sim pensei nisso, o estranho é que se eu não fizer isso ele não vai para o meu controller finaliza ele simplesmente é jogado para a pagina inicial.

vou tentar descobri porque ele esta tendo esse comportamento.

Se você acessar direto /login, imagino que ele vai redirecionar depois pra página que voce cadastrou como defaultSuccessUrl (pagina inicial). Mas tente ver o seguinte. Antes de logar, coloque itens no carrinho, e avance até chegar no /fechamento. Como ela está configurada só pra usuário logado, o security vai barrar e te levar pra página de login, depois do login ele deveria bater na /fechamento (tentativa interceptada) de novo.

Exatamente, seu acesso diretamente /login, apos o login ele vai para pagina defaultSuccessUrl , que eu havia colocado como meu controller, mas se eu retiro ele simplesmente ignora e volta para minha pagina inicial, quanto ao processo de fechamento do carrinho ele funciona ate eu fazer o login, depois me joga para pagina inicial sem nunca finalizar a compra.

Então gabriel a ideia não é tirar a defaultSuccessUrl. A ideia é deixar ela lá mas mandando pra uma página mais com cara de home

.and()
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/home")

A ideia é que o usuário pode fazer login em outros pontos que não na hora de fechar a compra. Exemplo: usuário acabou de entrar no site, clique em conta -> login, faz login. Nesse caso não faz sentido dizer que a url padrão de sucesso de login seria /fechamento. Ele deveria ir pra uma página central.

Isso não muda o fato de se o usuário tentar acessar /fechamento e não estiver logado, primeiro o security vai mandar o form pra ele logar, depois vai bater novamente em /fechamento que era pra onde ele estava indo.

Acredito que esse fluxo seria o mais natural, que é como muitos ecommerce fazem por aí.

Eu entendi rafael, minha ideia era que o usuario quando fizesse o login ele fosse redirecionado para uma pagina de conta, com os pedidos, o cadastro dele, a opção de enviar email e etc, eu coloquei o envio para o fechamento apenas para testar, porque se eu deixar sem ele volta para o inicio e não segue o fluxo da ultima requisição que seria o fechamento, isso acontece no curso, tanto que ele carrega o css, mas no meu projeto não esta indo para a ultima requisição, pode ser o meu formulário que não é o default do spring?

Acho difícil ser por causa do formulário. Da uma olhada no fluxo, como você faz a requisição, o que você está esperando no servidor e tal, pra investigar o que pode estar impedindo.

Rafael achei o erro, o action do botão que finalizava a compra estava errado, arrumei e agora ele bate no meu controller finaliza.

valeu cara abraço.