1
votes

I've been following a guide but I can't get Spring Security to work. It looks like it is authenticating but not authorizing or viceversa, or not redirecting to the login successful page. Maybe it is a stupid mistake but I can't see it.

My spring security config:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private Environment env;

    @Autowired
    private UserSecurityService userSecurityService;

    private static final String[] PUBLIC_MATCHERS = { 
        "/webjars/**", 
        "/css/**", 
        "/js/**", 
        "/images/**", 
        "/",
        "/about/**", 
        "/contact/**", 
        "/error/**/*",
        "/h2-console/**"
    };

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        List<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
        // Required by h2 console to work
        if(activeProfiles.contains("dev")) {
            http.csrf().disable();
            http.headers().frameOptions().disable();
        }
        http
            .authorizeRequests()
            .antMatchers(PUBLIC_MATCHERS).permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin().loginPage("/login").defaultSuccessUrl("/payload")
            .failureUrl("/login?error").permitAll()
            .and()
            .logout().permitAll();
    }

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userSecurityService);   
    }    
}

The application-dev.properties

spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=
hibernate.dialect=org.hibernate.dialect.H2Dialect

The logs:

DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@2dafa81d: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@2cd90: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 0D60174BBA25377F65443D95DB72F713; Granted Authorities: ROLE_ANONYMOUS

DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@7a27baf6, returned: 1

DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Authorization successful

DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - RunAsManager did not change Authentication object

DEBUG o.s.security.web.FilterChainProxy - /js/scripts.js reached end of additional filter chain; proceeding with original chain

DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.

DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally

DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed

DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.

DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally

DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed

1
The authentication process does throw an error. Although being downvoted (wasn't me, although reasonable), I think the question/problem might be useful especially for those coming from spring-security <=4. However, the question misses the problem description and could use some cleanup as almost all of the information provided does not help in understanding the problem at allfateddy

1 Answers

4
votes

During authentication the application throws the following error:

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

This exception is thrown, because the (plain text) password provided is missing the password-encoder {id}-prefix. Spring Security 5 now stores passwords using the following format (this was not the case for previous versions of spring security):

{id}encodedPassword

So that means for plain-text passwords, the {noop} id tells spring to match passwords using a NoOpPasswordEncoder (which basically handles passwords as plain-text).

However, storing plain-text passwords is highly discouraged (although it might be useful for automated testing).

Use a password encoder instead

Use of a BCryptPasswordEncoder, Pbkdf2PasswordEncoder or SCryptPasswordEncoder is highly recommended.

BCryptPasswordEncoder

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
class Config {

    @Bean
    public PasswordEncoder passwordEncoder() {
        // Create an encoder with strength 31
        // values from 4 .. 31 are valid; the higher the value, the more work has to be done to calculate the hash
        return new BCryptPasswordEncoder(12);
    }
}

Security Config

@Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    PasswordEncoder passwordEncoder;

    ...

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userSecurityService)
            .passwordEncoder(passwordEncoder);
    }
}

Encoding the password

@Service
class UserService implements UserDetailsService {
    private UserRepository userRepository;
    private PasswordEncoder passwordEncoder;

    UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }    

    User createUser(String username, String password) {
        // encrypt the plain-text password
        String encodedPassword = passwordEncoder.encode(password);
        User user = new User(username, encodedPassword));
        //...
        return userRepository.save(user);
    }
}

Supporting more than one encoder

To support more than one encoder, one might want to look at the DelegatingPasswordEncoder and PasswordEncoderFactories.

For further details have a look at https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-storage-format