2
votes

I have problem with validating user credentials. When I give correct credentials first time everything goes OK but giving invalid credentials first and then give correct ones I get invalid credentials error. I use Postman Basic Auth.

My config class:


    @Configuration
    @EnableWebSecurity
    public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        private UserService userService;

        @Autowired
        private CustomAuthenticationEntryPoint authenticationEntryPoint;

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

               http.cors().and().csrf().disable().authorizeRequests()
                    .antMatchers(HttpMethod.POST ,"/login").permitAll()
                    .antMatchers("/admin").hasAuthority("ADMIN")
                    .anyRequest().authenticated().and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.ALWAYS).and()
                    .logout()
                    .deleteCookies("remove")
                    .invalidateHttpSession(true);

               http.rememberMe().disable();

        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                    auth.userDetailsService(this.userService)
                            .and().eraseCredentials(true);
        }

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

And my controller class

   



     @PostMapping
            public ResponseEntity loginButtonClicked(HttpServletRequest request) {
                HttpSession session = request.getSession();
                final String authorization = request.getHeader("Authorization");
                String[] authorizationData=null;
                if (authorization != null && authorization.startsWith("Basic")) {
                    // Authorization: Basic base64credentials
                    String base64Credentials = authorization.substring("Basic" .length()).trim();
                    String credentials = new String(Base64.getDecoder().decode(base64Credentials),
                            Charset.forName("UTF-8"));
                    // credentials = username:password
                    authorizationData = credentials.split(":", 2);
                    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(authorizationData[0], authorizationData[1],Arrays.asList(new SimpleGrantedAuthority("USER")));
                    User user = userService.findUserEntityByLogin(authorizationData[0]);
                    if(user != null && user.getFromWhenAcceptLoginAttempts() != null && (user.getFromWhenAcceptLoginAttempts()).isBefore(LocalDateTime.now())){
                        // Authenticate the user
                        Authentication authentication = authenticationManager.authenticate(authRequest);
                        SecurityContext securityContext = SecurityContextHolder.getContext();
                        securityContext.setAuthentication(authentication);

                        // Create a new session and add the security context.
                        session = request.getSession();

                        session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);

                        return new ResponseEntity(new LoginResponseObject(200,"ACCESS GRANTED. YOU HAVE BEEN AUTHENTICATED"), HttpStatus.OK);
                    }else{
                        session.getId();
                        SecurityContextHolder.clearContext();
                        if(session != null) {
                            session.invalidate();
                        }
                        return new ResponseEntity(new ErrorObject(403,"TOO MANY LOGIN REQUESTS","YOU HAVE ENTERED TOO MANY WRONG CREDENTIALS. YOUR ACCOUNT HAS BEEN BLOCKED FOR 15 MINUTES.", "/login"), HttpStatus.FORBIDDEN);
                    }
                }else{
                    session.getId();
                    SecurityContextHolder.clearContext();
                    if(session != null) {
                        session.invalidate();
                    }
                    return new ResponseEntity(new ErrorObject(401,"INVALID DATA","YOU HAVE ENTERED WRONG USERNAME/PASSWORD CREDENTIALS", "/login"), HttpStatus.UNAUTHORIZED);
                }

            } 
            @Bean
            public AuthenticationManager authenticationManagerBean() throws Exception {
                return super.authenticationManagerBean();
            }

            @Bean
            public ObjectMapper objectMapper(){
                return new ObjectMapper();
            }

            @Bean
            public HttpSessionEventPublisher httpSessionEventPublisher() {
                return new HttpSessionEventPublisher();
            } 

2
try to debug, maybe username/password remain the same after first attemptAlmazini
Yup, it remains the same but I changed username and password in postmanThomas
Remove session policy from spring security config.Sumesh TG
authorizationData = credentials.split(":", 2); => Are you sure? If credentials contains user:password, I don't know what is credentials.split(":", 2);Arnaud Denoyelle
@SumeshTG It doesn't work for me.Thomas

2 Answers

4
votes

The problem is that the request is stored in cache due to your sessionCreationPolicy.

To avoid this problem, you could add .requestCache().requestCache(new NullRequestCache()) in your http security config to override the default request cache configuration, but be careful because this could create another side effect (it depends on your application).

In case you do not need the session, you can choose another session policy:

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).

Another alternative is to relay in Spring's BasicAuthenticationFilter. This filter does all the authentication logic for you. To enable it, you only have to add .httpBasic()in your http security configuration.

You may want to add a custom logic on authentication success/failure. In that case, you only have to create a custom filter (CustomBasicAuthenticationFilter) that extends BasicAuthenticationFilter class and overrides the methods onSuccessfulAuthentication()and onUnsuccessfulAuthentication(). You will not need to add .httpBasic() but you will need to insert your custom filter in the correct place:

.addFilterAfter(new CustomBasicAuthenticationFilter(authenticationManager), LogoutFilter.class).

Any of that 3 solutions will avoid your problem.

0
votes

Try to write .deleteCookies("JSESSONID") in your SpringSecurityConfig class.