how to log a user out programmatically using spring security - java

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.

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());

All you need is modify equals() and hashCode(). Your code will run later on. This could be helpful: http://blog.trifork.com/2014/02/28/session-timeout-and-concurrent-session-control-with-spring-security-and-spring-mvc/

Related

Spring security delete user - session still active

I got a simple spring security application with a user administration.
An admin should be able to create/update/delete users on the database (via hibernate).
If a user is updated, I am reloading the authentication of the user which is currently logged in. That's done with the following code (according to this example):
SecurityContextHolder.getContext().setAuthentication(updatedAuthentication);
My question is:
What can I do if a user is deleted? If I delete a user, already active sessions remain active and I don't know how to update them. I can still navigate to every page I was able to go to before.
Is there a way to tell spring that a session should be revalidated or something like that? Did I miss anything important?
On each request you should check your database for User existence.
Steps :
Take the userid from session, check it is in the database or not.
If not in the database invalidate the session and redirect to login page again.
Wrap those above two stpes in a method and call it on each request. (If common method is there use that or create e Listener)
Also you can check the following link if it helps. http://forum.spring.io/forum/spring-projects/security/35809-how-to-let-admin-to-force-user-to-logout
Another helpful link is http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#list-authenticated-principals
SecurityContextRepository
From Spring Security 3.0, the job of loading and storing the security context is now delegated to a separate strategy interface
You can provide a NullSecurityContextRepository in order to avoid the storage of security context information.
I did something like this:
#EnableWebSecurity
public class CustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Other security configuration...
http.securityContext().securityContextRepository(new NullSecurityContextRepository());
}
}

Difference between session handling locally and on server with Google App Engine

I am using sessions and have enabled sessions in my GAE app
<sessions-enabled>true</sessions-enabled>
<async-session-persistence enabled="true" />
I am using sessions for the purpose of a simple user login. Locally, this works just fine, my session is maintained until I logout and all my pages that are "protected" are viewable with a valid session.
The problem with my live/production server on appspot is that it doesn't work at all. When I login and authenticate, I redirect to another page. This page checks if I have a valid session (using standard HttpSession) and somehow this fails and then redirects me back to the login screen.
Does anyone have any idea why it doesn't work in the GAE production environment but works just fine locally?
Here is the code I use to check validity of current session:
public static boolean isValidSession(HttpServletRequest request) {
return (request.isRequestedSessionIdValid());
}
Update:
I'm creating session ID like this:
public static void createNewSession(HttpServletRequest request, final String username) {
HttpSession session = request.getSession(true);
session.setAttribute("username", username);
}
There is no difference between handling sessions locally and in production as such on Google App Engine. They work the same in both the environment. The only difference that I can think of is that when you create sessions (say at or after Login) locally and you set some attribute in the session say the access level of the user, it will not change even when attribute's value change(say if it is pulled dynamically from some database where it got changed after Login) until you close the tab and Login again, however in production if the attribute changes in the database and then if you refresh the page it will take the new value from the database. Well that's in my experience. Hope it helps.

Allow only one session per user

We have a web-application developed using struts2, spring & hibernate.
The application needs a functionality that one user can login from only one browser.
Say if user x, is logged in on pc-1 browser ff, then he cannot be logged in from any other place.
I tried it by implemention session map and store the sessions in global map, but this fails when user logs off and tries to login again.
Even it fails critically if the user does not logs off and session time-outs, but the map is not cleared.
Any better idea to implement this functionality.
We do not want to obstruct the user to login but do not want users to exploit the application by allowing him to share the creditionals and allow multiple users with same login to happen.
Since you are already using Spring, I would recommend you to integrate your application with Spring Security.
Spring security lets you define maximum sessions allowed per user concurrently.
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
If set when user having valid session tries to login again it will inform user that maximum concurrent access is set to 1.
Read more at the reference documentation of Spring Security: v3.2.x, v4.2.x or v5.1.x.
If spring security is not an option for you then:
Use a SessionInterceptor which will check for session validity, if session is valid it will check if user is already logged in to the application (for this you will have to maintain session somewhere for eg database for every successful login), if valid login is found, redirect user again to login page with custom message, or logout already valid session and then redirect him to login again. If you logout earlier session it would mean any successive action in that browser session will have to deal with invalid session.
If case you are also using Servlet in your application then Interceptor wont work for you, in this case you should use a Filter and follow the same steps as detailed above for Interceptor.
The best solution is to log-off user from other session when he logs in in new session. It is often that user would not logoff when closing browser and restricting him from logging in other window would be the pitfall.
Automaticly closing any previous user sessions is good, because in normal usage, it is no problem, but when sharing login and password, no two persons can work simultanously with your application.
At the login give the user a generated ID/cookie (sessionid suffices) stored with the user data. If a user does a request to the server with an old ID/cookie, say that he logged in elsewhere.
The other way round, forbidding the new login attempt, has its drawbacks - as you've experienced.
Create a map.
At the time of logging check that user id is present into that map or not.
If its not exist then put user id into map, at the time of logout remove that user id.
To be honest I would revisit the reasons why you have to restrict a user to a single login. Whilst preventing them from logging in from two different browsers is easy enough - any of the suggestions provided would work - with the Spring Security option being the easiest to implement if you can - they all break down when your user opens a second tab in the same browser. That is considered to be part of the same session.
Maintain user stack in servlet context,as it will be one for web container.perform a check before user getting logged in, if user name found in servlet context redirect him to login page.
All you should do is add a field in database userprofile table saying: alreadyLogin.
If user logins, make it Y. If user logs out, make it N. Now every time when user tries to login from new location, check this value and prevent login if Value is Y.
As many said, you can have a Map<String, User> (static Map or better an attribute in ServletContext) of (sessionId, user) of active users.
When a user tries to login, first check the existence in theMap.values(), and if it is okay add it to theMap.
Instead of removing from theMap on logout, implement a javax.servlet.http.HttpSessionListener, and on sessionDestroyed method, remove the item from it (the parameter of the method gives you the sessionId). This way if a user closes the browser, after session timeout period, it will be removed automatically.
On logout, invalidate the session, so it will be destroyed, and again this listener get executed.
Don't forget adding the listener to your web.xml.

How to check user details without log-in in Spring security

How to check the userdetails before spring security authentication is takes place.
Here in my project during login i need to check the basic validations username/password exists , validity of username , max retry limit(5invalid entries after that block a/c) and show error based on the failure.
On correct username/password need to check whether user logging into application for first time (based on flag in db) ,if so need to display an disclaimer page[ to access the application the user should accept the disclaimer].
Here is the flow
username/pwd in form -> Submit ->check for valid credentials
--> No --> display error[update retry flag]
--> yes -->check validity-->exceed->display error
-->with in validity period-->check is first time(and disclaimer accepted) -->show app if disclaimer already accepted else show disclaimer page without logging into application
On I accept button login the user to application.
I don't think this will involve Spring security per se. In your action class just call a method that checks the DB. If they have been logged in before return to an action that redirects the user to whatever page is appropriate. If they have not, then send them to the disclaimer page.
When they submit the form for the disclaimer page, check for acceptance. If accepted, then update the DB and redirect them to the appropriate page. If not, display an error.
If I recall Spring security correctly, the acceptance page and the login page, etc. will not be secured, but other pages will be. So, like I said, I'm not sure that Spring is really involved. It is just a matter of correctly routing via your Struts actions which do all the work of looking up in the DB and routing.
Basically, spring security takes care of it - but you can override it.
This is just a simple example how i would do it:
#Autowired
private AuthenticationManager authenticationManager;
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<?> login(#RequestBody AuthenticationRequest authenticationRequest, Device device) throws AuthenticationException {
// Perform the security
final Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword()
)
); ...
}
AutheticationRequest is just a simple object with 2 Strings inside (username, password)
Bare in mind that this method throws AuthenticationException but you can also catch it in try/catch and do some logic on failed login.
Hope it helps.

Programmatic use of Spring Security

I am using Wicket with the Wicket Auth Project for my presentation layer and I have therefore integrated it with Spring Security. This is the method which is called by Wicket for authentication for me:
#Override
public boolean authenticate(String username, String password) {
try {
Authentication request = new UsernamePasswordAuthenticationToken(
username, password);
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
} catch (AuthenticationException e) {
return false;
}
return true;
}
The contents (inside ) of my Spring Security XML configuration are:
<http path-type="regex">
<form-login login-page="/signin"/>
<logout logout-url="/logout" />
</http>
<global-method-security secured-annotations="enabled" />
<authentication-manager alias="authenticationManager"/>
<authentication-provider user-service-ref="userService">
<password-encoder ref="bcryptpasswordencoder" />
</authentication-provider>
The section 2.3.6. Session Fixation Attack Protection of the reference documentation says:
Session fixation attacks are a potential risk where it is possible
for a malicious attacker to create a
session by accessing a site, then
persuade another user to log in with
the same session (by sending them a
link containing the session identifier
as a parameter, for example). Spring
Security protects against this
automatically by creating a new
session when a user logs in. If you
don't require this protection, or it
conflicts with some other requirement,
you can control the behaviour using
the session-fixation-protection
attribute on , which has three
options:
migrateSession - creates a new session and copies the existing
session attributes to the new session. This is the default.
none - Don't do anything. The original session will be retained.
newSession - Create a new "clean" session, without copying the
existing session data.
The authentication works, but I as I'm fairly new to Spring Security I have some questions which I need answers too:
Normally for login, I would POST the authentication information to j_spring_security_check and let Spring Security perform the actual authentication code. I would like to have protection against session fixation attacks, will I get it when I perform a programmatic login as I do? And if not, what would I have to do to get it?
How do I perform programmatic logout?
As I will use programmatic login and logout, how do I disable Spring from intercepting those URL's?
Update:
For session fixation attack protection it seems that I need to call the method in the SessionUtils class with the signature startNewSessionIfRequired(HttpServletRequest request, boolean migrateAttributes, SessionRegistry sessionRegistry).
How do I get the SessionRegistry instance which I need to pass in? I can't find any way to create an alias ID for it, or how to get it's ID or name.
Maybe it's not a full answer to your questions, but maybe it might help you.
The code being called when you do NOT use programmatic login, but a standard one is to be found here:
org.springframework.security.ui.webapp.AuthenticationProcessingFilter
I guess you were inspired by this in your code. It looks quite similar.
Similarly the code executed when you access the /j_spring_security_logout in the standard approach, is to be found here:
org.springframework.security.ui.logout.LogoutFilter
The LogoutFilter calls multiple handlers. The handler we are using is called:
org.springframework.security.ui.logout.SecurityContextLogoutHandler, so you might call the same code in your approach.
You will indeed be open to session fixations attacks. To remedy this you could again be "inspired" by the Spring code. To create a new session you'll obviously need access to the httpsession so you may have to do some refactoring.
If you see the method SessionUtils.startNewSessionIfRequired.
This will migrate the authentication to a new session. You might be able to call this method directly or else just refactor the code a little.
As for programmatic logout you can't go too far wrong by simply calling session.invalidate() when you need to log the person out. This will do everything necessary from a general security perspective but bear in mind though you might need to cleanup some things on the session. If you have a very complicated set of filters etc. and you need to ensure that that the user is logged out for the rest of the request then you could add:
SecurityContextHolder.getContext().setAuthentication(null);
As for interception of the url's you could just set them to something unused and ignore it! I'm not sure if you can turn off the interception in configuration - if you really want to remove it then have a look at the AuthenticationProcessingFilter - you could customise this. If you do this then you'll have to manually setup the spring security xml and not use the provided namespaces. It's not too hard though - look at some older documentation and you'll see how to do this.
Hope this helps!
1) Programmatic Logout
call HttpServletRequest.getSession(false).invalidate
call SecurityContextHolder.clearContext()
2) Tell Spring Security NOT to intercept certain URLs, this one kind of depends on how your application url space is setup. If all your pages (except /logIn and /logout) lived at the context /myApp then you could do this:
<http ....>
<intercept-url pattern="/myApp/**" ..>
....
</http>
I had an issue with programmatic login. I called all the authenticationManager.authenticate(...) and SecurityContextHolder.getContext().setAuthentication(...) methods but had some issues with the Session. I had to add the following lines to properly manage the session:
HttpSession session = request.getSession();
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
This was not clear from the example code posted above. For more look at http://forum.springsource.org/showthread.php?t=69761
To do programmatic logout it's also possible to throw an org.springframework.security.core.AuthenticationException. For example, SessionAuthenticationException. In this case ExceptionTranslationFilter initiate logout.
You can try this
try {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
SecurityContextHolder.clearContext();
} catch (Exception e) {
logger.log(LogLevel.INFO, "Problem logging out.");
}

Categories

Resources