简体   繁体   中英

Spring security login always lands in an error page with no message

I'm using a login form for a small project in Spring but I have a small problem which is that every time I log in using a sign in form I get an error redirection.

This is my SecurityConfiguration.java

package com.ffuentese;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private DataSource dataSource;

    @Value("${spring.queries.users-query}")
    private String usersQuery;

    @Value("${spring.queries.roles-query}")
    private String rolesQuery;

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.
            jdbcAuthentication()
                .usersByUsernameQuery(usersQuery)
                .authoritiesByUsernameQuery(rolesQuery)
                .dataSource(dataSource)
                .passwordEncoder(bCryptPasswordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.
            authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/login").permitAll()
                .antMatchers("/registration").permitAll()
                .antMatchers("/**").hasAuthority("ADMIN").anyRequest()
                .authenticated().and().csrf().disable().formLogin()
                .loginPage("/login").failureUrl("/login?error=true")
                .defaultSuccessUrl("/home")
                .usernameParameter("email")
                .passwordParameter("password")
                .and().logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/").and().exceptionHandling()
                .accessDeniedPage("/access-denied");
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web
           .ignoring()
           .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
    }

}

My sign in form:

<form th:action="@{/login}" method="POST" class="form-signin">
    <h3 class="form-signin-heading" th:text="Welcome"></h3>
    <br/>

    <input type="text" id="email" name="email"  th:placeholder="Email"
        class="form-control" /> <br/> 
    <input type="password"  th:placeholder="Password"
        id="password" name="password" class="form-control" /> <br /> 

    <div align="center" th:if="${param.error}">
        <p style="font-size: 20; color: #FF1C19;">Email or contraseña errónea, por favor intente nuevamente.</p>
    </div>
    <button class="btn btn-lg btn-primary btn-block" name="Submit" value="Login" type="Submit" th:text="Login"></button>
</form>

My loginController.java

package com.ffuentese;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.ffuentese.User;

@Controller
public class LoginController {

    @Autowired
    private UserService userService;

    @RequestMapping(value={"/", "/login"}, method = RequestMethod.GET)
    public ModelAndView login(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("login");
        return modelAndView;
    }

    @RequestMapping(value="/home", method = RequestMethod.GET)
    public ModelAndView homeV(){
        ModelAndView modelAndView = new ModelAndView();
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        modelAndView.setViewName("home");
        return modelAndView;
    }


    @RequestMapping(value="/registration", method = RequestMethod.GET)
    public ModelAndView registration(){
        ModelAndView modelAndView = new ModelAndView();
        User user = new User();
        modelAndView.addObject("user", user);
        modelAndView.setViewName("registration");
        return modelAndView;
    }

    @RequestMapping(value = "/registration", method = RequestMethod.POST)
    public ModelAndView createNewUser(@Valid User user, BindingResult bindingResult) {
        ModelAndView modelAndView = new ModelAndView();
        User userExists = userService.findUserByEmail(user.getEmail());
        if (userExists != null) {
            bindingResult
                    .rejectValue("email", "error.user",
                            "There is already a user registered with the email provided");
        }
        if (bindingResult.hasErrors()) {
            modelAndView.setViewName("registration");
        } else {
            userService.saveUser(user);
            modelAndView.addObject("successMessage", "User has been registered successfully");
            modelAndView.addObject("user", new User());
            modelAndView.setViewName("registration");

        }
        return modelAndView;
    }

    @RequestMapping(value="/admin/home", method = RequestMethod.GET)
    public ModelAndView home(){
        ModelAndView modelAndView = new ModelAndView();
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        User user = userService.findUserByEmail(auth.getName());
        modelAndView.addObject("userName", "Welcome " + user.getName() + " " + user.getLastName() + " (" + user.getEmail() + ")");
        modelAndView.addObject("adminMessage","Content Available Only for Users with Admin Role");
        modelAndView.setViewName("admin/home");
        return modelAndView;
    }


}

So, instead of going from login to /home it ends up landing in /error. The error itself is a line of code like this:

{"timestamp":"2018-04-04T21:28:28.944+0000","status":999,"error":"None","message":"No message available"}

The form does work because if I move from /error and go to any protected URL they can be opened.

EDIT: The original code comes from this repository and I adapted it to my own project https://github.com/gustavoponce7/SpringSecurityLoginTutorial also explained here

EDit: Another point I think it's important is that if I login and then login again the form seems to work alright taking the user from login to /home as expected. It's odd.

You can add the always-use-default-target param to the success URL.

.defaultSuccessUrl("/home",true)

This means that if the login is successful you will always be sent to /home .

I think the undesirable behaviour is caused by the error page being queued for some reason, and then when the login is successful you get "returned" to that.

This isn't the ideal solution but providing you don't want to go to the previous page after logging in, then it works to prevent your described behaviour.

Maybe this is about that, there is no RequestMapping with Param error. Possible solution

@RequestMapping(value={"/", "/login"}, method = RequestMethod.GET)
    public ModelAndView login(@RequestParam(value = "error", required = false)){
        ModelAndView modelAndView = new ModelAndView();
        if (error != null) {
          modelAndView.setViewName("error page");
        } else modelAndView.setViewName("login");

        return modelAndView;
    }

Edit1

Might be also caused by not having all following folders in your project "/static/**", "/js/**", "/css/**", "/img/**", "/json/**" , delete this configure or add all folders.

I guess the question is answered, I faced this error today and I had a different situation. I thought it would help some other new spring coders as I had to google for a while.

It is necessary to add the attribute name to username or password text fields. After adding the attribute name="username" and name="password" in corresponding input types, you must be able to login.

Spring uses the name parameter to do the matching of username and password. so its a must! this may seem simple, but this is how it works

The answer suited in my case, too, has been that shared by 123 .

The following piece of advice at https://www.baeldung.com/spring-security-login came in handy as well:

One reason to override most of the defaults in Spring Security is to hide the fact that the application is secured with Spring Security and minimize the information a potential attacker knows about the application.

The following at https://docs.spring.io/spring-security/site/docs/4.2.12.RELEASE/apidocs/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.html is also useful to refer to:

defaultSuccessUrl(String defaultSuccessUrl) Specifies where users will go after authenticating successfully if they have not visited a secured page prior to authenticating.

defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) Specifies where users will go after authenticating successfully if they have not visited a secured page prior to authenticating or alwaysUse is true.

如果您是第一次使用默认身份验证,则创建一个控制器并处理默认请求(即获取 '/' )

In my case there were some other requests that caused this to happen (one of them was 404 when loading css resource). After fixing them this error disappeared.

EDIT

To resolve the issue properly you should exclude /error page from spring security filters. Check Spring security redirects to page with status code 999 for details

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM