Grails , how do I get an object NOT to save - java

I am new to grails and trying to create a form which allows a user to change the email address associated with his/her account for a site I am creating.
It asks for the user for their current password and also for the new email address they want to use.
If the user enters the wrong password or an invalid email address then it should reject them with an appropriate error message.
Now the email validation can be done through constraints in grails, but the password change has to match their current password. I have implemented this check as a method on a service class.
See code below:
def saveEmail =
{
def client = ClientUser.get(session.clientUserID)
client.email = params.email
if(clientUserService.checkPassword(session.clientUserID , params.password) ==false)
{
flash.message = "Incorrect Password"
client.discard()
redirect(action:'changeEmail')
}
else if(!client.validate())
{
flash.message = "Invalid Email Address"
redirect(action:'changeEmail')
}
else
{
client.save();
session.clientUserID = null;
flash.message = "Your email address has been changed, please login again"
redirect(controller: 'clientLogin' , action:'index')
}
}
Now what I noticed that was odd was that if I entered an invalid email then it would not save the changes (as expected) BUT if I entered the wrong password and a valid email then it would save the changes and even write them back into the database even though it would give the correct "invalid password" error message.
I was puzzled so set break points in all the if/else if/else blocks and found that it was hitting the first if statement as expected and not hitting the others , so it would never come accross a call to the save() method, yet it was saved anyway.
After a little research I came accross documentation for the discard() method which you can see used in the code above. So I added this but still no avail. I even tried using discard then reloading the client object from the DB again but still no dice.
This is very frustrating and I would be grateful for any help, since I think that this should surely not be a complicated requirement!

Grails closes your Hibernate session at the end of the web request, which will flush out the changed object. The object's connected to your Hibernate session because you got hold of it via Hibernate (get()). If you want to avoid having the change flushed, you need to use discard().
This is done automatically by a failing validator, which is why you're not having to do it for a validation fail.
You'd simplify the code, however, by either moving this logic to a custom validator on one of your ClientUser fields, which would automatically discard the object on failure, or by using a Grails command object, which could also encapsulate the verification logic. Then you'd just check for errors on the command object.

Using get() method retrieve data in read-write mode, so if you change anything it will be persist.Use read() method that can retrieve data in read-only mode so if you change anything then need to use save() method.That will resolve your problem.
Take a look at this http://grails.org/doc/latest/guide/GORM.html#basicCRUD

Use read method that retrieves an instance of the domain class for the specified id in a read-only state.
See the Grails documentation about read.

You are just doing flash which has nothing to do with rolling back transaction.
In the event of error throw RuntimeException and it will roll back your changes to the database.

After an R & D, I figure out that we need to clear a whole session like:
session.clear()
According to the documentation :
Completely clear the session. Evict all loaded instances and cancel
all pending saves, updates and deletions. Do not close open iterators
or instances of ScrollableResults.
We need to properly cleanup the hibernate session. In result the session is return to usable state and for future use it will not impact by the earlier error.

Related

Wicket: double login required due to extended browser info

I have a Wicket 8.6 application. Currently, when logging in to the application, mostly (does not always happen) the user has to login twice. After the first login (after entering the credentials and clicking the submit button) a white page appears saying "If you see this, it means that both javascript and meta-refresh are not support by your browser configuration. Please click this link to continue to the original destination." This is the BrowserInfoPage. After a few seconds the user is redirected to the login page again where he/she has to enter his/her credentials again and press the login button. This time, the user logs in successfully. My question is, how do I prevent that the user hast to enter his/her credentials twice.
From my research I know that it has something to do with the collection of extended browser info. In the init method of my WicketApplication class, I had the following code:
getRequestCycleSettings().setGatherExtendedBrowserInfo(true);
However, I already commented out this code several month ago. For some reason, the described effect occurs for every new deploy now. Maybe a newly added package in the application is the reason for it. I don't know. Is there a possibility to prevent this second login maybe by creating a customized bowser info page which forwards the login? Please point me in the right direction. Thanks.
After some research, I came up with a work around. It is probably not very efficient but it works for me so far. In my custom Session class which inherits from AuthenticatedWebSession, I added the following code.
#Override
protected WebPage newBrowserInfoPage() {
final Request request = RequestCycle.get().getRequest();
if(request.getUrl().toString().contains("LoginPage")) {
if(!isSignedIn()) {
signIn(username, password);
}
PageParametersEncoder encoder = new PageParametersEncoder();
PageParameters parameters = encoder.decodePageParameters(request.getUrl());
String url = parameters != null && parameters.get("originUrl") !=null && !parameters.get("originUrl").isNull() && !parameters.get("originUrl").isEmpty()?
parameters.get("originUrl").toString("pages/home"):"pages/home";
String finalUrl=url.startsWith("pages/")?url.substring("pages/".length()):url;
throw new RedirectToUrlException(finalUrl);
}
return super.newBrowserInfoPage();
}
Some explanation to the code. As mentioned in the question, I want to prevent the user from logging in multiple times. Thus, I check if the request comes from the LoginPage and perform my work around only in that case.
During my implementation, I realized, that the method newBrowserInfoPage is called in the process when I call session.signIn(username,password); on my LoginPage. In this signIn process the authenticate method of my custom Session is called but the signedIn flag in the AuthenticatedWebSession is not changed (keeps false on successfull authentication). Is this a bug? Thus, I have to login again to set the flag to true.
Finally, I read the URL of the LoginPage where I have stored the target URL and forward the user to the target URL.
I am aware this is probably not the best approach but it is the only solution I came up with. If someone has a better idea, I am happy to hear it.

Java Session Management in multiple tabs of same browser [duplicate]

In a web-application implemented in java using JSP and Servlets; if I store information in the user session, this information is shared from all the tabs from the same browser. How to differ sessions in the browser-tabs?
In this example:
<%#page language="java"%>
<%
String user = request.getParameter("user");
user = (user == null ? (String)session.getAttribute("SESSIONS_USER") : user);
session.setAttribute("SESSIONS_USER",user);
%>
<html><head></head><body>
<%=user %>
<form method="post">
User:<input name="user" value="">
<input type="submit" value="send">
</form>
</body></html>
Copy this code in a jsp page (testpage.jsp), deploy this file in an existing context of a web application on the server (I use Apache Tomcat), then open a browser (FF, IE7 or Opera) using the correct URL (localhost/context1/testpage.jsp), type your name in the input and submit the form. Then open a new tab in the same browser, and then you can see your name (get from the session) on the new tab. Be careful with the browser-cache, sometimes seems that it doesn't happen, but it's in the cache, refresh the second tab.
Thanks.
You can use HTML5 SessionStorage (window.sessionStorage). You will generate a random id and save in session Storage per Browser Tab.
Then each browser tab has his own Id.
Data stored using sessionStorage do not persist across browser tabs,
even if two tabs both contain webpages from the same domain origin. In
other words, data inside sessionStorage is confined to not just the
domain and directory of the invoking page, but the browser tab in
which the page is contained in. Contrast that to session cookies,
which do persist data from tab to tab.
You have to realize that server-side sessions are an artificial add-on to HTTP. Since HTTP is stateless, the server needs to somehow recognize that a request belongs to a particular user it knows and has a session for. There are 2 ways to do this:
Cookies. The cleaner and more popular method, but it means that all browser tabs and windows by one user share the session - IMO this is in fact desirable, and I would be very annoyed at a site that made me login for each new tab, since I use tabs very intensively
URL rewriting. Any URL on the site has a session ID appended to it. This is more work (you have to do something everywhere you have a site-internal link), but makes it possible to have separate sessions in different tabs, though tabs opened through link will still share the session. It also means the user always has to log in when he comes to your site.
What are you trying to do anyway? Why would you want tabs to have separate sessions? Maybe there's a way to achieve your goal without using sessions at all?
Edit:
For testing, other solutions can be found (such as running several browser instances on separate VMs). If one user needs to act in different roles at the same time, then the "role" concept should be handled in the app so that one login can have several roles. You'll have to decide whether this, using URL rewriting, or just living with the current situation is more acceptable, because it's simply not possible to handle browser tabs separately with cookie-based sessions.
The window.name Javascript property, is the only thing that will persist across tab activity, but can remain independent (instead of URL guff).
You shouldn't. If you want to do such a thing either you need to force user to use a single instance of your application by writing URLs on the fly use a sessionID alike (not sessionid it won't work) id and pass it in every URL.
I don't know why you need it but unless you need make a totally unusable application don't do it.
I've come up with a new solution, which has a tiny bit of overhead, but seems to be working so far as a prototype. One assumption is that you're in an honour system environment for logging in, although this could be adapted by rerequesting a password whenever you switch tabs.
Use localStorage (or equivalent) and the HTML5 storage event to detect when a new browser tab has switched which user is active. When that happens, create a ghost overlay with a message saying you can't use the current window (or otherwise disable the window temporarily, you might not want it to be this conspicuous.) When the window regains focus, send an AJAX request logging the user back in.
One caveat to this approach: you can't have any normal AJAX calls (i.e., ones that depend on your session) happen in a window that doesn't have the focus (e.g. if you had a call happening after a delay), unless you manually make an AJAX re-login call before that. So really all you need do is have your AJAX function check first to make sure localStorage.currently_logged_in_user_id === window.yourAppNameSpace.user_id, and if not, log in first via AJAX.
Another is race conditions: if you can switch windows fast enough to confuse it, you may end up with a relogin1->relogin2->ajax1->ajax2 sequence, with ajax1 being made under the wrong session. Work around this by pushing login AJAX requests onto an array, and then onstorage and before issuing a new login request, abort all current requests.
The last gotcha to look out for is window refreshes. If someone refreshes the window while you've got an AJAX login request active but not completed, it'll be refreshed in the name of the wrong person. In this case you can use the nonstandard beforeunload event to warn the user about the potential mixup and ask them to click Cancel, meanwhile reissuing an AJAX login request. Then the only way they can botch it is by clicking OK before the request completes (or by accidentally hitting enter/spacebar, because OK is--unfortunately for this case--the default.) There are other ways to handle this case, like detecting F5 and Ctrl+R/Alt+R presses, which will work in most cases but could be thwarted by user keyboard shortcut reconfiguration or alternative OS use. However, this is a bit of an edge case in reality, and the worst case scenarios are never that bad: in an honour system configuration, you'd be logged in as the wrong person (but you can make it obvious that this is the case by personalizing pages with colours, styles, prominently displayed names, etc.); in a password configuration, the onus is on the last person who entered their password to have logged out or shared their session, or if this person is actually the current user, then there's no breach.
But in the end you have a one-user-per-tab application that (hopefully) just acts as it should, without having to necessarily set up profiles, use IE, or rewrite URLs. Make sure you make it obvious in each tab who is logged into that particular tab, though...
I'll be honest here. . .everything above may or may not be true, but it all seems WAY too complicated, or doesn't address knowing what tab is being used server side.
Sometimes we need to apply Occam's razor.
Here's the Occam's approach: (no, I'm not Occam, he died in 1347)
assign a browser unique id to your page on load. If, and only if, the window doesn't have an id yet (so use a prefix and a detection)
on every page you have (use a global file or something) simply put code in place to detect the focus event and/or mouseover event. (I'll use jquery for this part, for ease of code writing)
in your focus (and/or mouseover) function, set a cookie with the window.name in it
read that cookie value from your server side when you need to read/write tab specific data.
Client side:
//Events
$(window).ready(function() {generateWindowID()});
$(window).focus(function() {setAppId()});
$(window).mouseover(function() {setAppId()});
function generateWindowID()
{
//first see if the name is already set, if not, set it.
if (se_appframe().name.indexOf("SEAppId") == -1){
"window.name = 'SEAppId' + (new Date()).getTime()
}
setAppId()
}
function setAppId()
{
//generate the cookie
strCookie = 'seAppId=' + se_appframe().name + ';';
strCookie += ' path=/';
if (window.location.protocol.toLowerCase() == 'https:'){
strCookie += ' secure;';
}
document.cookie = strCookie;
}
server side (C# - for example purposes)
//variable name
string varname = "";
HttpCookie aCookie = Request.Cookies["seAppId"];
if(aCookie != null) {
varname = Request.Cookies["seAppId"].Value + "_";
}
varname += "_mySessionVariable";
//write session data
Session[varname] = "ABC123";
//readsession data
String myVariable = Session[varname];
Done.
We had this problem and we solved it very easy. I mean easy because no programming involved.
What we wanted to do was to let a user login to multiple account within same browser window without conflicting the sessions.
So the solution was random subdomains.
23423.abc.com
242234.abc.com
235643.abc.com
So we asked our system admin to configure the SSL certificates for *.abc.com rather abc.com
Then with little code change, every time a user try to login, he gets logged in a tab with a random subdomain number. so each tab could have its own session independently.
Also to avoid any conflict, we developed the random number using a hash or md5 of user id.
You can use link-rewriting to append a unique identifier to all your URLs when starting at a single page (e.g. index.html/jsp/whatever). The browser will use the same cookies for all your tabs so everything you put in cookies will not be unique.
I think what you probably want is to maintain navigation state across tabs and not specifically creating a single session per tab. This is exactly what the Seam framework achieves with their Conversation scope/context. Their implementation relies on the fact that a conversation id is propagated with each request and creates the notion of a conversation on the server side, which is something that lies between a session and a request. It allows for navigation flow control and state management.
Although that's mainly aimed at JSF, have a look and check if that's something where you can take some ideas from: http://docs.jboss.org/seam/latest/reference/en-US/html_single/#d0e3620
In javascript, how can I uniquely identify one browser window from another which are under the same cookiedbased sessionId
Essentially use window.name. If its not set, set it to a unique value and use it. It will be different across tabs that belong to same session.
Note: The solution here needs to be done at application design stage. It would be difficult to engineer this in later.
Use a hidden field to pass around the session identifier.
For this to work each page must include a form:
<form method="post" action="/handler">
<input type="hidden" name="sessionId" value="123456890123456890ABCDEF01" />
<input type="hidden" name="action" value="" />
</form>
Every action on your side, including navigation, POSTs the form back (setting the action as appropriate). For "unsafe" requests, you could include another parameter, say containing a JSON value of the data to be submitted:
<input type="hidden" name="action" value="completeCheckout" />
<input type="hidden" name="data" value='{ "cardNumber" : "4111111111111111", ... ' />
As there are no cookies, each tab will be independent and will have no knowledge of other sessions in the same browser.
Lots of advantages, particularly when it comes to security:
No reliance on JavaScript or HTML5.
Inherently protects against CSRF.
No reliance on cookies, so protects against POODLE.
Not vulnerable to session fixation.
Can prevent back button use, which is desirable when you want users to follow a set path through your site (which means logic bugs that can sometimes be attacked by out-of-order requests, can be prevented).
Some disadvantages:
Back button functionality may be desired.
Not very effective with caching as every action is a POST.
Further information here.
Another approach that works is to create a unique window id and store this value along with the session id in a database table. The window id I often use is integer(now). This value is created when a window is opened and re-assigned to the same window if the window is refreshed, reloaded or submitted to itself. Window values (inputs) are saved in the local table using the link. When a value is required, it is obtained from the database table based on the window id / session id link. While this approach requires a local database, it is virtually foolproof. The use of a database table was easy for me, but I see no reason why local arrays would not work just as well.
Spring Session supports multiple session in same browser
Look at the samples and implementation detail
http://docs.spring.io/spring-session/docs/current/reference/html5/guides/users.html
I resolved this of following way:
I've assigned a name to window this name is the same of connection resource.
plus 1 to rid stored in cookie for attach connection.
I've created a function to capture all xmloutput response and assign sid and rid to cookie in json format. I do this for each window.name.
here the code:
var deferred = $q.defer(),
self = this,
onConnect = function(status){
if (status === Strophe.Status.CONNECTING) {
deferred.notify({status: 'connecting'});
} else if (status === Strophe.Status.CONNFAIL) {
self.connected = false;
deferred.notify({status: 'fail'});
} else if (status === Strophe.Status.DISCONNECTING) {
deferred.notify({status: 'disconnecting'});
} else if (status === Strophe.Status.DISCONNECTED) {
self.connected = false;
deferred.notify({status: 'disconnected'});
} else if (status === Strophe.Status.CONNECTED) {
self.connection.send($pres().tree());
self.connected = true;
deferred.resolve({status: 'connected'});
} else if (status === Strophe.Status.ATTACHED) {
deferred.resolve({status: 'attached'});
self.connected = true;
}
},
output = function(data){
if (self.connected){
var rid = $(data).attr('rid'),
sid = $(data).attr('sid'),
storage = {};
if (localStorageService.cookie.get('day_bind')){
storage = localStorageService.cookie.get('day_bind');
}else{
storage = {};
}
storage[$window.name] = sid + '-' + rid;
localStorageService.cookie.set('day_bind', angular.toJson(storage));
}
};
if ($window.name){
var storage = localStorageService.cookie.get('day_bind'),
value = storage[$window.name].split('-')
sid = value[0],
rid = value[1];
self.connection = new Strophe.Connection(BoshService);
self.connection.xmlOutput = output;
self.connection.attach('bosh#' + BoshDomain + '/' + $window.name, sid, parseInt(rid, 10) + 1, onConnect);
}else{
$window.name = 'web_' + (new Date()).getTime();
self.connection = new Strophe.Connection(BoshService);
self.connection.xmlOutput = output;
self.connection.connect('bosh#' + BoshDomain + '/' + $window.name, '123456', onConnect);
}
I hope help you
I've been reading this post because I thought I wanted to do the same thing. I have a similar situation for an application I'm working on. And really it's a matter of testing more than practicality.
After reading these answers, especially the one given by Michael Borgwardt, I realized the work flow that needs to exist:
If the user navigates to the login screen, check for an existing session. If one exists bypass the login screen and send them to the welcome screen.
If the user (in my case) navigates to the enrollment screen, check for an existing session. If one exists, let the user know you're going to log that session out. If they agree, log out, and begin enrollment.
This will solve the problem of user's seeing "another user's" data in their session. They aren't really seeing "another user's" data in their session, they're really seeing the data from the only session they have open. Clearly this causes for some interesting data as some operations overwrite some session data and not others so you have a combination of data in that single session.
Now, to address the testing issue. The only viable approach would be to leverage Preprocessor Directives to determine if cookie-less sessions should be used. See, by building in a specific configuration for a specific environment I'm able to make some assumptions about the environment and what it's used for. This would allow me to technically have two users logged in at the same time and the tester could test multiple scenarios from the same browser session without ever logging out of any of those server sessions.
However, this approach has some serious caveats. Not least of which is the fact that what the tester is testing is not what's going to run in production.
So I think I've got to say, this is ultimately a bad idea.
Storing the timeStamp in window.sessionStorage if it is not already set.
This will give a unique value for each tab(even if the URLs are same)
http://www.javascriptkit.com/javatutors/domstorage.shtml
https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage
Hope this helps.
How to differ sessions in browser-tabs?
The most straightforward way to differ sessions in browser tabs is to disallow your particular domain to set cookies. That way, you can have separate sessions from separate tabs. Say you disallow cookies from this domain: www.xyz.com. You open Tab 1, login and start browsing. Then you open Tab 2, and you can login either as a same user or a different one; either way, you will have a session separate from Tab 1. And so on.
But of course this is possible when you have control over the client side. Otherwise, the solutions prescribed by the folks here should apply.
you will need to do
1- store a cookie for accounts list
2- optional store a cookie for default one
3- store for each account with it's index like acc1, acc2
4- put in the url something represent the index of accounts and if not you will select the default one
like google mail domain.com/0/some-url >> 0 here represent the index of account
also you may need to know how to use urlwrite
5- when select a cookie, select it according to your urlpath represent the account index
Regards
I see many implementations which have client side changes to manipulate session id cookies. But in general session id cookies should be HttpOnly so java-script cannot access otherwise it may lead to Session Hijack thru XSS
If it's because each tab will be running a different flow in your application, and mixing both flows causes problems, then it's better to "Regionalize" your session objects, so that each flow will use a different region of the session
This region can be implemented as simply as having different prefixes for each flow, or session object will hold multiple maps (one for each flow), and you use those maps instead of session attributes, the best though would be to extend your session class and use it instead.

Verify Azure Table SAS-based credentials

I'm looking for simple way of verifying an arbitrary Azure Table connection string that uses a SAS such as the one below using the Azure Storage's Java SDK:
https://example.table.core.windows.net/example?sig=aaabbbcccdddeeefffggghhh%3D&se=2020-01-01T00%3A00%3A00Z&sv=2015-04-05&tn=example&sp=raud
I tried a bunch of different methods exposed by the CloudTable api, but none of them works.
CloudTable.exists() throws a StorageException, regardless of whether the credentials are valid
getName(), getStorageUri(), getUri(), and other getters - all work locally, regardless of the credentials
getServiceClient().downloadServiceProperties() and getServiceClient().getServiceStats() also throw various exceptions, while getServiceClient().getEndpoint() and getServiceClient().getCredentials() and others always work locally.
Why don't I just query the Table for a row or two? Well, in many cases I need to verify a SAS that gives only write or update premissions (without delete or read permissions), and I do not want to execute a statement that changes something in the table just to check the credentials.
To answer your questions:
CloudTable.exists() throws a StorageException, regardless of whether
the credentials are valid
I believe there's a bug with the SDK when using this method with SAS Token. I remember running into the same issue some time back.
getName(), getStorageUri(), getUri(), and other getters - all work
locally, regardless of the credentials
These will work as they don't make network call. They simply use the data available to them in the different instance variables and return the data.
getServiceClient().downloadServiceProperties() and
getServiceClient().getServiceStats() also throw various exceptions,
while getServiceClient().getEndpoint() and
getServiceClient().getCredentials() and others always work locally.
In order for getServiceClient().someMethod() to work using SAS, you would need Account SAS instead of Service SAS (which you're using right now).
Why don't I just query the Table for a row or two? Well, in many cases
I need to verify a SAS that gives only write or update premissions
(without delete or read permissions), and I do not want to execute a
statement that changes something in the table just to check the
credentials.
One possible way to check the validity of a SAS Token for write operation is to perform a write operation which you know will fail with an error. For example, you can try to insert an entity which is already there. In this case, you should get a Conflict (409) error. Other thing you could try to do is perform an optimistic write by specifying some random Etag value and check for Precondition Failed (412) error. If you get a 403 error or 404 error, that would indicate there's something wrong with your SAS token.

Preventing Multiple Login with same login credentials

I am developing a web application that needs to prevent multiple login using the same user name and password concurrently.
If it happens on the same machine then we need to do something with the user session, but it should also prevent if they are login on different machines using the same user name and password.
What can be the best approach :-
1) should i store the user session,credentials,and IPAddress of the machine in the DB.
2) should we use the session tracking mechanism in the application itself.If so what is the best approach?
Also , We have to keep following things in mind:
1) If user close the browser without logout.
2) If session times out.
Hope it clears the question.
Besdies data base hits (which could fail if your server is broguth down without updating db) : A data base friendly way is not to hit the data base for every re login or you could get denial of service attacks that brig you dowm. Instead implement a session listener in J2EE and check if same user is logged in by looking up the user id in a cache.
If you have more than one app node then need a distributed cache with a time out same as session time out in web.xml.
Simply have a field in your database that has text that says online or offline for each user, according to whether they are logged in or not. So when someone tries to log in with that username, check the database if the field says online for that given user on submit. If the field says online, don't allow log in. Otherwise permit it.
without using a database
you can store if a user is online in a text file
$check= "onlineCheck.txt";
$fh = fopen($check, 'a') or die("can't open file");
$nowOnline= "USER678 \n";
fwrite($fh, $nowOnline);

how to detect if user is logged in to site from another browser?

Scenario:
User logs into website.com using firefox. Login credentials are valid, user is directed to member's page.
User tries to log in to website.com using chrome. Login credentials are valid, because use is already logged in using firefox, system will throw error, asks user to close other session to login through chrome.
How can I detect that? Right now I am able to detect it if user only use one browser, but seems to break when user uses two different browsers to log in at different times.
EDIT* I want to say it's more than just using different browsers, the website should not allow multiple people to log in with the same login credentials.
I am assuming your application is j2EE/servlet based. If it is the case, two browsers are independent of each other, hence they have their own sessionId and can function independently, as long as your application does not interfere.
To prevent this scenario, one way to implement is, keep a hashmap of SessionID and UserID in your servlet. You populate this on every successful login, for example via a filter or a valve. When you are populating the hashmap, make a check, to see if any other sessionID is already using this userID. If it is used, check if the corresponding sessionID is still active. If it is not active, allow the login, and delete the stale sessionID. If it is active, terminate the other session and allow the login.
If you're using Spring Security - it may be specified by parameter in the configuration file.
If just plain java - during log-in put user's session id to some storage; when he tries to log-in again you should prohibit it.
But you need to avoid situation when the user will be in storage very long time after closing the browser (one possible solution is short session timeout + keep-alive requests)
In your application, keep a timeout of the user that's updated after each call to the app. You can define the user as 'locked' into a session (for example your firefox session) until either the timeout expires, or the user requests a logout. When you log in on another browse (for example, chrome) it checks to see if there's an active session and, if there is, denies the login attempt.
I'm going to make up a quick example. This isn't even close to production ready and is for illustrative purposes only.
class User {
long lastCheckin;
int userId;
String username;
}
Now, when someone does anything in the app, like viewing a page, you do this
user.lastCheckin = System.currentTimeMillis();
Now, when someone specifically requests a logout, you do this
user.lastCheckin = 0L;
Now, when someone tries to log in, you do this
if(user.lastCheckin + PREDEFINED_TIMEOUT > System.currentTimeMillis()) {
return new LoginResponse(false,"User is active in another session.");
}
You can store a map of logged-in users on an application scope variable like a ServletContext. For example, on your auth servlet, you can probably do something like:
Map<String,String> activeUsers = request.getSession().getServletContext().getAttribute("__ONLINE_AUTHENTICATED_USERS");
//if null, context hasn't been prepared yet, create and attach a new one?2
You have to be careful though. Being an application scope variable, you need to ensure some thread safety and this is something which the servletContext.setAttribute/getAttribute is providing(e.g. those operations are not thread safe). You may be able to handle this by using some sort of application lifecycle listener to 'initialize' the servletContext to have the user map. This way you won't need to worry about the set/getAttribute. You still need to think about the map operations themselves(e.g. use j.u.c.ConcurrentHashMap maybe?).
You also have to take care of cleaning up(e.g. removing from the map) when a user logs out or session times out.
You also have to consider that a user might lock himself out for a long time by this approach(e.g. close browser but do not logout properly, session needs to timeout before the mapping is cleared).
Edit:
You also need to think about scalability and this depends on your application. Are you expecting a million online users? Or only a couple of thousands?

Categories