Ad

After Implementing Spring Security In My (java Spring Mvc + Mysql Application, Thymeleaf) Something Weird Happens With The Authentication

having custom userdetails service. If I enter a url that is not listed in my permit all list in the security configuration. it will be redirected to the login and processed as an error login. Then after that directly if I signin with correct user and password, it will not be redirected to the default success url, it will be going to the previous wrong url I entered before. What does it mean? what is wrong with my code. Please help me!

Here is my user details service implementation

   @Service
   public class UserDetailsServiceImpl implements UserDetailsService{
   @Autowired
   private UserRepository userRepository;

  @Override
  @Transactional(readOnly = true)
  public UserDetails loadUserByUsername(String email) throws 
  UsernameNotFoundException {

   User user = userRepository.findByEmail(email);
           Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
          for (Role role : user.getRoles()){
          grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
    }
    return new org.springframework.security.core.userdetails.User(user.getEmail(), 
 user.getPassword(), grantedAuthorities);

}

here is my Security Configuration class

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

 @Autowired
 private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        auth
            .userDetailsService(userDetailsService)
            .passwordEncoder(bCryptPasswordEncoder);
    }

 @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }


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

    http.
        authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/signin").permitAll()

            .antMatchers("/confirm").permitAll()
            .antMatchers("/index").permitAll()
            .antMatchers("/adminpage").permitAll()
            .antMatchers("/register").permitAll()
            .antMatchers("/login").permitAll()

   .antMatchers("/library/**","/admin").hasAuthority("ADMIN").anyRequest()
            .authenticated().and().csrf().disable().formLogin()
            .loginPage("/login").failureUrl("/login?error=true")
            .defaultSuccessUrl("/index")
            .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/**", 
"/img/**","/fonts/**")
 ;  }
Ad

Answer

This behaviour is due to SavedRequestAwareAuthenticationSuccessHandler. As described in javadoc api of class SavedRequestAwareAuthenticationSuccessHandler:

An authentication success strategy which can make use of the DefaultSavedRequest which may have been stored in the session by the ExceptionTranslationFilter. When such a request is intercepted and requires authentication, the request data is stored to record the original destination before the authentication process commenced, and to allow the request to be reconstructed when a redirect to the same URL occurs. This class is responsible for performing the redirect to the original URL if appropriate.

Following a successful authentication, it decides on the redirect destination, based on the following scenarios:

  • If the alwaysUseDefaultTargetUrl property is set to true, the defaultTargetUrl will be used for the destination. Any DefaultSavedRequest stored in the session will be removed.
  • If the targetUrlParameter has been set on the request, the value will be used as the destination. Any DefaultSavedRequest will again be removed.
  • If a SavedRequest is found in the RequestCache (as set by the ExceptionTranslationFilter to record the original destination before the authentication process commenced), a redirect will be performed to the Url of that original destination. The SavedRequest object will remain cached and be picked up when the redirected request is received (See SavedRequestAwareWrapper). If no SavedRequest is found, it will delegate to the base class.

If you want to skip this behaviour and allways redirect to default success url, you could achieve that just using method defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) instead of just defaultSuccessUrl(String defaultSuccessUrl), and of course, setting second parameter as boolean, just this way:

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

    http.
        authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/signin").permitAll()

            .antMatchers("/confirm").permitAll()
            .antMatchers("/index").permitAll()
            .antMatchers("/adminpage").permitAll()
            .antMatchers("/register").permitAll()
            .antMatchers("/login").permitAll()

   .antMatchers("/library/**","/admin").hasAuthority("ADMIN").anyRequest()
            .authenticated().and().csrf().disable().formLogin()
            .loginPage("/login").failureUrl("/login?error=true")
            .defaultSuccessUrl("/index", true)              
            .usernameParameter("email")
            .passwordParameter("password")
            .and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/").and().exceptionHandling()
            .accessDeniedPage("/access-denied");

}

This is the source code of methods defaultSuccessUrl(String defaultSuccessUrl) and defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) from Spring security's AbstractAuthenticationFilterConfigurer class in github repo:

/**
     * Specifies where users will go after authenticating successfully if they have not
     * visited a secured page prior to authenticating. This is a shortcut for calling
     * {@link #defaultSuccessUrl(String)}.
     *
     * @param defaultSuccessUrl the default success url
     * @return the {@link FormLoginConfigurer} for additional customization
     */
    public final T defaultSuccessUrl(String defaultSuccessUrl) {
        return defaultSuccessUrl(defaultSuccessUrl, false);
    }

    /**
     * Specifies where users will go after authenticating successfully if they have not
     * visited a secured page prior to authenticating or {@code alwaysUse} is true. This
     * is a shortcut for calling {@link #successHandler(AuthenticationSuccessHandler)}.
     *
     * @param defaultSuccessUrl the default success url
     * @param alwaysUse true if the {@code defaultSuccesUrl} should be used after
     * authentication despite if a protected page had been previously visited
     * @return the {@link FormLoginConfigurer} for additional customization
     */
    public final T defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) {
        SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
        handler.setDefaultTargetUrl(defaultSuccessUrl);
        handler.setAlwaysUseDefaultTargetUrl(alwaysUse);
        return successHandler(handler);
}
Ad
source: stackoverflow.com
Ad