Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to log a user out programmatically using spring security

i am using spring security v3.1.4. what i want to achieve is to have an admin be able to log out a regular user (invalidate his session). a user can only log in once at any given time, but if he forgets to log out, then when he attempts to log in from another location, he won't be able to log in. so he'll put in a ticket to the admin, and the admin will invalidate all his previously logged in sessions (hopefully there's just one).

in my web.xml i have the following defined.

<listener>
 <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

in my spring security xml i have the following defined.

<session-management invalid-session-url="/home">
 <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" session-registry-ref="sessionRegistry"/>
</session-management>
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>

i then have a rest-like controller to perform the logging out.

@Controller
@RequestMapping("/api/admin")
public class RestAdminController {
 static final Set<SimpleGrantedAuthority> AUTHS = new HashSet<>();
 static {
  AUTHS.add(new SimpleGrantedAuthority("ROLE_USER"));
 }

 @Autowired
 private SessionRegistry sessionRegistry;

 @RequestMapping("/user/logout");
 public @ResponseBody String logout(@RequestBody Account account) {
  User user = new User(account.getUsername(), "", AUTHS);
  List<SessionInformation> infos = sessionRegistry.getAllSessions(u, false);

  for(SessionInformation info : infos) {
   info.expireNow(); //expire the session
   sessionRegistry.removeSessionInformation(info.getSessionId()); //remove session
  }

  return "ok";
 }
}

this code "kinda" works when i test it from the same computer. let's say we have a user, USER_A, and an administrator, ADMIN_A.

  • USER_A uses chrome to log into the APP.
  • USER_A uses firefox to log into the APP. he is denied because a user can only have 1 login session at a time.
  • ADMIN_A goes in, and calls the rest-like service (code above) to "kick" out all of USER_A's session.
  • USER_A can now use firefox to log into the APP.

however, USER_A is now logged in twice, once in chrome and once in firefox.

  • refreshing the (spring security protected) pages for USER_A in chrome (first log in) doesn't force him to be redirected (to the login page).
  • refreshing the protected pages for USER_A in firefox (second login) also doesn't force him to be redirected.

any idea on approaches on how to completely invalidate/destroy USER_A's first/previous login sessions such that if he tries to access protected pages spring security will know, "hey this guy's session is invalid or expired, send him to the login page" ?

any help is appreciated. thanks.

like image 971
Jane Wayne Avatar asked Mar 13 '14 06:03

Jane Wayne


1 Answers

It looks like you've almost got it, but I think the problem is that you are removing the information prematurely from the SessionRegistry. The ConcurrentSessionFilter performs a check on the current session when a user makes a request, and at this point, it logs out an expired session and invalidates it. Since you have already removed the information for that session, it won't find it and will do nothing.

Try removing the line:

sessionRegistry.removeSessionInformation(info.getSessionId());
like image 111
Shaun the Sheep Avatar answered Nov 26 '22 19:11

Shaun the Sheep