Concurrent User lockout with Spring Security -
i thought had issue resolved post. however, found new use case causing issues, please pardon duplication. have spring-boot application uses spring-security 4.0.2. not allow concurrent logins, , can confirm cannot login twice in separate browsers. see browser apparently shares session can login 2 different tabs of same browser (chrome, safari, firefox, ie11 tested).
what seeing when attempt third browser tab login, authentication fails. expected , desired behavior, not acceptable user account locked out , server restart required. examine database records , user account still has enabled.
i set breakpoints , stepped thru org.springframework.security.core.sessionsessionregistryimpl , org.springframework.security.web.authenticationsimpleurlauthenticationfailurehandler , cannot see anywhere user being blocked. have debug levels:
logging.level.org.springframework:info logging.level.org.springframework.web: info logging.level.org.springframework.security:info logging.level.org.springframework.security.web.authentication:info and cannot see exceptions or error messages being thrown. here primary config:
@configuration @enablewebsecurity public class websecurityconfig extends websecurityconfigureradapter { @autowired private customuserdetailsservice customuserdetailsservice; @autowired private sessionregistry sessionregistry; @autowired servletcontext servletcontext; @autowired private customlogouthandler logouthandler; @autowired public void configureglobal(authenticationmanagerbuilder auth) throws exception { auth .userdetailsservice(customuserdetailsservice) .passwordencoder(passwordencoder()); return; } @override protected void configure(httpsecurity http) throws exception { http .addfilterafter(new csrftokengeneratorfilter(), csrffilter.class) .authorizerequests() .antmatchers("/", ).permitall().anyrequest().authenticated() .and() .formlogin() .loginpage("/loginpage") .permitall() .loginprocessingurl("/login") .defaultsuccessurl("/?tab=success").failureurl("/") .and() .logout().addlogouthandler(logouthandler).logoutrequestmatcher( new antpathrequestmatcher("/logout")) .deletecookies("jsessionid") .invalidatehttpsession(true).permitall().and().csrf() .and() // instructs spring security use servlet 3.1 changesessionid method protecting against session fixation attacks, // set maximum session 1 .sessionmanagement().sessionauthenticationstrategy( concurrentsessioncontrolauthenticationstrategy()).sessionfixation().changesessionid().maximumsessions(1) .maxsessionspreventslogin( true).expiredurl("/login?expired" ).sessionregistry(sessionregistry ) .and() .sessioncreationpolicy(sessioncreationpolicy.if_required) .invalidsessionurl("/") .and().authorizerequests().anyrequest().authenticated(); // here protect site from: // 1. x-content-type-options http.headers().contenttypeoptions(); // 2. web browser xss protection http.headers().xssprotection(); http.headers().cachecontrol(); http.headers().httpstricttransportsecurity(); // 3. x-frame-options http.headers().frameoptions(); // inject servlet context , set http (for increased security should customize http true) servletcontext.getsessioncookieconfig().sethttponly(true); } // cors protection @bean public webmvcconfigurer corsconfigurer() { return new webmvcconfigureradapter() { @override public void addcorsmappings(corsregistry registry) { registry.addmapping( "/**" ).allowedorigins( "*" ) .allowedheaders( "access-control-allow-origin", "*" ) .allowedheaders( "access-control-allow-headers", "x-requested-with" ) .allowedmethods( "get", "post", "put", "delete" ) .maxage( 3600 ); } }; } @bean public passwordencoder passwordencoder() { // default strength = 10 return new bcryptpasswordencoder(); } @bean public static servletlistenerregistrationbean httpsessioneventpublisher() { return new servletlistenerregistrationbean(new httpsessioneventpublisher()); } @bean public concurrentsessioncontrolauthenticationstrategy concurrentsessioncontrolauthenticationstrategy() { concurrentsessioncontrolauthenticationstrategy strategy = new concurrentsessioncontrolauthenticationstrategy(sessionregistry()); strategy.setexceptionifmaximumexceeded(true); strategy.setmessagesource(messagesource); return strategy; } } i have logouthandler handle sessions (i think quchie referring to?):
@component public class customlogouthandler implements logouthandler { private static final logger logger = loggerfactory.getlogger( customlogouthandler.class ); @autowired private auditservice auditservice; @autowired private sessionregistry sessionregistry; @override public void logout(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, authentication authentication) { if (authentication != null && authentication.getdetails() != null) { try { user user = (user)authentication.getprincipal(); auditservice.logoutuser(user.getoffice()); httpservletrequest.getsession().invalidate(); httpservletresponse.setstatus(httpservletresponse.sc_ok); //redirect login httpservletresponse.sendredirect(urls.root); list<sessioninformation> usersessions = sessionregistry.getallsessions(user, true); (sessioninformation session: usersessions) { sessionregistry.removesessioninformation(session.getsessionid()); } list<object> principals = sessionregistry.getallprincipals(); if (principals.contains(user)) { throw new exception("user not removed session!"); } logger.debug( "user logged out" ); } catch (exception e) { e.printstacktrace(); e = null; } } } } can me find out how prevent user account being locked on failed concurrent login attempt? or @ least make have time unlocks have time amount?
edit: cannot understand why locks out extended periods of time (days) if storing in session. database records never set bad state, yet user account locked out until restart tomcat.
Comments
Post a Comment