Getting "Maximum sessions of 1 for this principal exceeded" when logging in with different username

I configured my application to allow only single session per account. It works fine when i use JdbcDaoImpl provider. It also works fine when i use DaoAuthenticationProvider with custom User object extending spring User. But when i try to setup spring security with custom User object implementing UsersDetails interface i got the message above when I tried to log in using different account. I cannot figure out why.

Here is my security configuration :

<session-management invalid-session-url="/">
    <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>

<authentication-manager>
    <authentication-provider ref="daoAuthenticationProvider"/>      
</authentication-manager>

<beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
    <beans:property name="userDetailsService" ref="userDetailsServiceImplementation"></beans:property>
</beans:bean>

<beans:bean id="userDetailsServiceImplementation" class="com.company.service.implementation.UserServiceImpl" />

And my custom User object :

public class UserVo extends CommonVo implements UserDetails{
    private String username;
    private String password;
    private String firstName;
    private String lastName;
    private String enabled;

    private List<GrantedAuthority> userAuthorities;

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEnabled() {
        return enabled;
    }
    public void setEnabled(String enabled) {
        this.enabled = enabled;
    }
    public List<GrantedAuthority> getUserAuthorities() {
        return userAuthorities;
    }
    public void setUserAuthorities(List<GrantedAuthority> userAuthorities) {
        this.userAuthorities = userAuthorities;
    }       

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return userAuthorities;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return "Y".equals(enabled) ? true : false;
    }

}

And this is my UserDetailsService implementation :

public class UserServiceImpl implements UserService, UserDetailsService{

    @Autowired
    private UserDAO userDao;

    @Autowired
    private UserVo userVo;

    @Override
    public int insert(UserVo userVo) {
        return userDao.insert(userVo);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<UserVo> list(UserVo userVo) {
        return (List<UserVo>) userDao.select(userVo);
    }

    public List<String> listUserRoles(UserVo userVo) {
        return (List<String>) userDao.listUserRoles(userVo);
    }

    @SuppressWarnings("rawtypes")
    @Override
    public Map select(UserVo userVo) {
        return userDao.select(userVo);
    }


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        userVo.setUsername(username);

        @SuppressWarnings({ "rawtypes", "unchecked" })
        Map result = new HashMap(userDao.select(userVo));

        List<String> userRoles = userDao.listUserRoles(userVo);
        String sRoles = StringUtils.join(userRoles, ",");
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

        for(String role : userRoles){
            authorities.add(new SimpleGrantedAuthority(role));
        }

        userVo.setUsername(result.get("username").toString());
        userVo.setPassword(result.get("password").toString());
        userVo.setEnabled(result.get("enabled").toString());
        userVo.setUserAuthorities(authorities);

        //return new LoginVo(result.get("username").toString(), result.get("password").toString(), AuthorityUtils.commaSeparatedStringToAuthorityList(sRoles));
        return userVo;
    }

}

This works as expected if i return object that extends spring User class, in this case LoginVo.

For concurrency control Spring Security uses a SessionRegistry the default implementation uses a HashMap to store things. For a HashMap to work correctly you need to have a correctly implemented hashCode and equals method. If you don't (or always return a default value) it won't work correctly.

To solve simply implement a correct hashCode and equals method in your custom object.

Updates on Novel Corona Virus (COVID-19), The signs and symptoms of COVID-19 present at illness onset vary, but over the course of the disease, most persons with COVID-19 will experience the following: Fever (83–99%) Cough (59–82%) Fatigue (44–70%) Anorexia (40–84%) Shortness of breath (31–40%) Sputum production (28–33%) Myalgias (11–35%) Atypical presentations have been described, and older adults and persons with medical comorbidities may have delayed presentation of fever and respiratory symptoms. I Synonyms for getting at Thesaurus.com with free online thesaurus, antonyms, and definitions. Find descriptive alternatives for getting.

If anyone faces this issue in spring boot ,this is what you have to add in your security config file,Apart From M.Deinum Answer

//security configuration class for implementing spring security on urls
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService userDetailsService;
    //for handling user success handler
    @Autowired
    private CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
    @Override
    //this configuration is for handling user requests
    protected void configure(HttpSecurity http)  {
         try {
            http
                .authorizeRequests()
                .antMatchers("/orders").permitAll()
                .antMatchers("/createrole").permitAll()
                     .antMatchers("/login").permitAll()
                     .antMatchers("/admin/**").hasAuthority("admin")
                     .antMatchers("/agent/**").hasAuthority("agent")
                     .antMatchers("/distributor/**").hasAuthority("distributor")
                     .antMatchers("/home/**").hasAuthority("user").anyRequest()
                    .authenticated().and().csrf().disable().formLogin().successHandler(customizeAuthenticationSuccessHandler)
                    .loginPage("/login").failureUrl("/login?error=true")
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .and().logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    .logoutSuccessUrl("/logout.done").deleteCookies("JSESSIONID")
                    .invalidateHttpSession(true) 
                    .logoutSuccessUrl("/login").and().exceptionHandling().accessDeniedPage("/403");
            http.sessionManagement( ).maximumSessions(1). maxSessionsPreventsLogin(true);
            http.sessionManagement( ).sessionFixation( ).migrateSession( )
                    .sessionAuthenticationStrategy( registerSessionAuthStr( ) );

        } catch (Exception e) {
            // TODO Auto-generated catch block
            System.out.println("Exception here");
        }
    }


    //this method allows static resources to be neglected by spring security
    @Override
    public void configure(WebSecurity web) throws Exception {
        web
            .ignoring()
            .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**","/assets/**","/fonts/**","/dis/**","/vendor1/**","/mobile/**");
    }

    @Bean
    public SessionRegistry sessionRegistry( ) {
        SessionRegistry sessionRegistry = new SessionRegistryImpl( );
        return sessionRegistry;
    }
    @Bean
    public RegisterSessionAuthenticationStrategy registerSessionAuthStr( ) {
        return new RegisterSessionAuthenticationStrategy( sessionRegistry( ) );
    }
///Very important ,you wont login again after logout if you dont include this
    @Bean
    public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
        return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)  {
         //BCryptPasswordEncoder encoder = passwordEncoder();

    try {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    } catch (Exception e) {

        System.out.println("Login Failed");
    }
} 


}

What you should know about COVID-19, The virus can cause a range of symptoms, ranging from mild illness to pneumonia. Symptoms of the disease are fever, cough, sore throat and headaches. In severe cases difficulty in breathing and deaths can occur. getting definition: 1. present participle of get 2. present participle of get . Learn more.

Ok, i've solved my problem by following @m-deinum answer in the comment section of my original question.

What i did is generate hashCode() and equals() by right clicking eclipse -> Source -> Generate hashCode() and equals() in both UserVo and CommonVo.

It turns out that i have to override both method above if i have my own implementation of UsersDetail interface

WHO Director-General's opening remarks at the media briefing on COVID-19, Wear a cloth face covering that covers your nose and mouth in public settings. Clean and disinfect frequently touched surfaces. Wash your hands often with soap and water for at least 20 seconds, or use an alcohol- based hand sanitizer that contains at least 60% alcohol. 261 synonyms of getting from the Merriam-Webster Thesaurus, plus 366 related words, definitions, and antonyms. Find another word for getting. Getting: to acquire complete knowledge, understanding, or skill in. Synonyms: learning, mastering, picking up…

I'm developing a normal spring application [Configured without xml] without any boot Configurations...

i overrided my hascode and equal methods in the customer.

I not able to login after 5 or 10 attempts getting msg.[Maximum sessions of 1 for this principal exceeded]

I don't have this method, how to implement in a normal application.

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

in Console-

Session is destroyed -- Session is newly created --- Session is destroyed --- Session is newly created -- Session is destroyed -- Session is newly created-- Session is newly created ---- Info Anonymous User.....................

Getting Synonyms, Getting Antonyms, They found that for people with mild disease, recovery time is about two weeks, while people with severe or critical disease recover within three to six weeks. get: [verb] to gain possession of. to receive as a return : earn.

getting, Synonyms for getting at Thesaurus.com with free online thesaurus, antonyms, and definitions. Find descriptive alternatives for getting. Synonyms for getting in Free Thesaurus. Antonyms for getting. 1 synonym for getting: acquiring. What are synonyms for getting?

Prevent Getting Sick, Coronavirus: Is the pandemic getting worse in the US? By Mike Hills Visual journalist. 19 June 2020. Share this with Facebook; Share this with Messenger  Set in a geriatric extended care wing of a down-at-the-heels hospital, Getting On follows put-upon nurses, anxious doctors and administrators as they struggle with the darkly comic, brutally honest and quietly compassionate realities of caring for the elderly.

Use Cloth Face Coverings to Help Slow Spread, Restaurant menus are getting smaller. Here's what some big chains are dropping​. By Danielle Wiener-Bronner, CNN Business. Updated 3:54  Deposits to an inmate’s trust account, as well as probation, community corrections, and background check payments are provided by TouchPay Holdings, LLC d/b/a GTL Financial Services (NMLS #967396), which is also the owner and manager of this website.

Comments
  • How is your equals and hashCode implemented?
  • I didn't implement equals and hashCode, is that necessary here? sorry i'm fairly new at Java
  • Does your CommonVo have them implemented? And yes you need them as the default concurrency mechanism is done in memory using a HashMap if you have a crappy implementation of the hashCode and/or equals it won't work properly.
  • Thanks)), i implemented both hashCode and equals in CommonVo and UserVo as you suggested which i haven't done it before, and now it works great. I generate hashCode and equals from eclipse, I wish i can mark your comment as accepted.
  • Please explain what you did as an answer and accept it - so others can learn from you - or give @m-deinum the honor to transform his comment into an answer - that way he receives some reputation for it. Leaving it open without answer just annoys everyone checking if you still need help. Welcome to Stack Overflow.