Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

Solucionado
(ver solução)
12
respostas

Request method 'POST' not supported

Após toda configuração quando faço o login tenho o erro: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported

minha configuração

http.authorizeRequests()
            .antMatchers("/assets/**", 
                    "/fonts/**",
                    "/global/**", 
                    "/imagens/**", 
                    "/auth/**").permitAll()
            .anyRequest().authenticated()
        .and()
            .formLogin()
            .loginPage("/auth/form")
            .failureUrl("/auth/login?error=true")
            .usernameParameter("j_username")
            .passwordParameter("j_passsword")
            .permitAll()
        .and()
            .logout()
            .logoutSuccessUrl("/auth/login?logout=true")
            .invalidateHttpSession(true)
            .deleteCookies("JSESSIONID")
        .and()
            .exceptionHandling().accessDeniedPage("/auth/denied");

Controller de acesso:

@Controller
@RequestMapping(value = "auth")
public class LoginController {

    @RequestMapping(value = "/form", method = RequestMethod.GET)
    public String loginPage(){
        return "login";
    }

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public ModelAndView login(
            @RequestParam(value = "error", required = false) boolean error,
            @RequestParam(value = "error", required = false) boolean logout, ModelMap model
            ){
        System.out.println("===========");

        if(error){
            model.addAttribute("error", "Login inválido, senha ou nome do usuário não confere.");
        return new ModelAndView("login", model);
        }

        if(logout){
            model.addAttribute("logout", "Deslogado com sucesso");
            return new ModelAndView("login", model);
        }

        return new ModelAndView("redirect:/");
    }

    @RequestMapping(value = "/denied", method = RequestMethod.GET)
    public ModelAndView acessoNegado(){

        return new ModelAndView("error", "mensagem", "Acesso negado");
    }
}

jsp

<c:url value="/auth/login" var="loginUrl"/>
      <form method="POST" action="${loginUrl }">

          <c:if test="${error != null}">
              ${error}
          </c:if>
           <c:if test="${logout != null}">
              ${logout}
          </c:if>
        <!-- <div class="form-group">
          <label class="sr-only" for="inputName">Nome</label>
          <input type="text" class="form-control" id="inputName" name="j_username" placeholder="Name">
        </div> -->
        <div class="form-group">
          <label class="sr-only" for="inputEmail">Email</label>
          <input type="email" class="form-control" id="inputEmail" name="j_username" placeholder="Email">
        </div>
        <div class="form-group">
          <label class="sr-only" for="inputPassword">Senha</label>
          <input type="password" class="form-control" id="inputPassword" name="j_password"
          placeholder="Password">
        </div>
        <div class="form-group clearfix">
          <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token }"/>
          <div class="checkbox-custom checkbox-inline checkbox-primary float-left">
            <input type="checkbox" id="inputCheckbox" name="remember">
            <label for="inputCheckbox">Lembrar</label>
          </div>
          <a class="float-right" href="forgot-password.html">Esqueci minha senha!</a>
        </div>
        <button type="submit" class="btn btn-primary btn-block">Enviar</button>
      </form>
12 respostas

pelo login padrão do spring consigo fazer o login tranquilamente, mas como o meu não!

        http.authorizeRequests()
            .antMatchers("/assets/**", 
                    "/fonts/**",
                    "/global/**", 
                    "/imagens/**", 
                    "/auth/**").permitAll()
            .anyRequest().authenticated()
        .and()
            .formLogin()
            /*.loginPage("/auth/form")
            .failureUrl("/auth/login?error=true")
            .usernameParameter("username")
            .passwordParameter("passsword")
            .permitAll()
        .and()
            .logout()
            .logoutSuccessUrl("/auth/login?logout=true")
            .invalidateHttpSession(true)
            .deleteCookies("JSESSIONID")
        .and()
            .exceptionHandling().accessDeniedPage("/auth/denied")*/;

Ola Clerman, o problema ocorre pois no seu controller voce indicou que que aceita apenas metodos GET, mas voce está fazendo um post na sua tela de login:

Na anotacao @RequestMapping, troca o atributo method para RequestMethod.POST, conforme abaixo:

@RequestMapping(value = "/login", method = RequestMethod.POST)
    public ModelAndView login(
            @RequestParam(value = "error", required = false) boolean error,
            @RequestParam(value = "error", required = false) boolean logout, ModelMap model
            )

Ok, aparentemente esta certo, mas ao fazer o login o redirecionamento volta ao login

pelo login padrão do spring consigo fazer o login tranquilamente, mas como o meu não! Como posso resolver?

Olá Clerman, ele retorna a tela de login pois é necessário avisar o spring que o usuário está logado, mas nesse caso usar o controller nao vai dar muito certo, o correto é criar um novo provider de autenticação do spring. Nesse projeto do github tem um exemplo de como implementar:

https://github.com/denisricci/treld-blog-api/blob/master/src/main/java/br/com/treld/config/security/SpringSecurityConfig.java

Se é que eu entendi bem a sua resposta Denis, minha intenção é justamente utilizar o controller para customizar completamente a tela de login, erros e todos os retornos do login para o usuário, não quero utilizar o padrão, preciso saber por que não estou conseguindo acessar com meu login customizado e com o padrão estou conseguindo?

Olá Clerman,

Eu entendi que voce deseja customizar totalmente a tela de login, porém utilizar o controller não é o melhor caminho, conforme eu falei, o spring precisa ficar sabendo que o usuário está logado e precisa criar uma sessao para ele, para fazer isso, voce terá que criar um Autenticator Provider e indicar ao spring como ele deve autenticar o usuário, voce pode criar sua propria pagina de login, mas deve enviar os dados utilizando o próprio endpoint do spring.

Infelizmente eu nao tenho um exemplo melhor para te passar, mas repare que na classe abaixo, a busca to usuário é totalmente customizada, mas é necessario indicar ao Spring utilizar essa classe para fazer a autenticacao.

@Service
public class MongoDBAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    private static final Logger LOG = LoggerFactory.getLogger(MongoDBAuthenticationProvider.class);

    @Autowired
    private AuthorRepository authorRepository;


    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        Assert.notNull(userDetails.getPassword(), "Password of user " + userDetails.getUsername() + " is null");

        String password = authentication.getCredentials().toString();

       if(!CryptographyUtils.checkEquality(password, userDetails.getPassword())){
           throw new UsernameNotFoundException("Authentication Failed");
       }

    }

    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {

        User author = authorRepository.findByUsername(username);

        if(author == null){
            String errorMessage = "Author with username " + username + " not found";
            LOG.debug(errorMessage);
            throw new UsernameNotFoundException(errorMessage);
        }

        return new AuthorUserDetails(author);
    }
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

    @Autowired
    private CustomAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private CustomAuthenticationFailureHandler restAuthenticationFailureHandler;

    @Autowired
    private MongoDBAuthenticationProvider authenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws TreldException {
        try{
            http.
                    csrf().disable().
                    exceptionHandling().
                    authenticationEntryPoint(restAuthenticationEntryPoint).
                    and().formLogin().
                    successHandler(customAuthenticationSuccessHandler).
                    failureHandler(restAuthenticationFailureHandler);
        }catch(Exception e){
            throw new TreldException(e);
        }
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws TreldException {
        try{
            auth.authenticationProvider(authenticationProvider);
        }catch(Exception e){
            throw new TreldException(e);
        }
    }
}

Denis Ricci fico grato mas, fico com receio de utilizar a forma que propunha, pois vou ter que encaixar essa MongoDBAuthenticationProvider e criar todas essas outras classes para injetar na classe de configuração, ficaria ruim de desse algum bug, ou algum erro, devido a minha pouca experiência ainda fico com essa insegurança!

Sem problemas Clerman,

Lembrando que o MongoDBAuthentication é apenas um exemplo, se voce pesquisar um pouco na internet voce encotrara outros exemplos de como implementar o seu login.

Quando altero para o login padrão do Spring, funciona normalmente. Tem um outra sugestão do que posso fazer?

http.authorizeRequests() .antMatchers("/assets/", "/fonts/", "/global/", "/imagens/", "/usuario/", "/auth/", "/usuario/").permitAll() .antMatchers("/").hasAnyAuthority("VISITANTE") .anyRequest().authenticated() .and() .formLogin() //.loginPage("/auth/login") //.failureUrl("/auth/login?error=true") //.usernameParameter("j_username") //.passwordParameter("j_passsword") .permitAll() .and() .logout() .logoutSuccessUrl("/auth/login?logout=true") .invalidateHttpSession(true) .deleteCookies("JSESSIONID") .and() .exceptionHandling().accessDeniedPage("/auth/denied");

Denis encontrei uma solução aqui mas não encontrei o problema que acho que esta no ModelAndView (acho!), pois troquei os métodos e o form e ainda alterei a configuração no form, gostaria de entender o que ocorreu? Se puder ajudar!

@Controller
//@RequestMapping(value = "auth")
public class LoginController {

    /*@RequestMapping(value = "/login", method = RequestMethod.GET)
    public String loginPage(){
        return "login";
    }*/

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login(Model model, String error, String logout) {
        if (error != null)
            model.addAttribute("error", "Nome de usuário ou senha inválidos.");

        if (logout != null)
            model.addAttribute("messagem", "Você foi deslogado com sucesso.");

        return "login";
    }

    /*@RequestMapping(value = "/login", method = RequestMethod.POST)
    public ModelAndView login(
            @RequestParam(value = "error", required = false) boolean error,
            @RequestParam(value = "error", required = false) boolean logout, ModelMap model
            ){
        if(error){
            model.addAttribute("error", "Login inválido, senha ou nome do usuário não confere.");
        return new ModelAndView("login", model);
        }

        if(logout){
            model.addAttribute("logout", "Deslogado com sucesso");
            return new ModelAndView("login", model);
        }

        return new ModelAndView("redirect:/");
    }*/

    /*@RequestMapping(value = "/denied", method = RequestMethod.GET)
    public ModelAndView acessoNegado(){

        return new ModelAndView("error", "mensagem", "Acesso negado");
    }*/
}
<form method="POST" action="/financeiro/login">
        <div class="form-group ${error != null ? 'has-error' : ''}">
            <span>${messagem}</span>
            <input name="username" type="text" class="form-control" placeholder="Nome"
                   autofocus="true"/>
            <input name="password" type="password" class="form-control" placeholder="Senha"/>
            <span>${error}</span>
            <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

            <button class="btn btn-lg btn-primary btn-block" type="submit">Log In</button>
        </div>
    </form>
http.authorizeRequests()
            .antMatchers("/assets/**", "/fonts/**", "/global/**", "/imagens/**", "/usuario/**", "/auth/**", "/usuario/**").permitAll()
            .antMatchers("/**").hasAnyAuthority("VISITANTE")
            .anyRequest().authenticated()
        .and()
            .formLogin()
            .loginPage("/login")
            .permitAll()
        .and()
            .logout()
            .logoutSuccessUrl("/login")
            .invalidateHttpSession(true)
            .deleteCookies("JSESSIONID")
        .and()
            .exceptionHandling().accessDeniedPage("/denied");
    }
solução!

Olá Clerman,

Seu método esta mapeado para receber requisicoes GET porém voce esta fazendo um POST para o endpoint.