Is it possible to assign a custom ID to a HTTP session through Servlet API?
I know that session handling from any application server, Tomcat for example, it's enough good to generate unique IDs. But I have custom unique session IDs based on information per user and time, so it won't be repeated.
And I looked at every documentation about session handling but nowhere I find what I need.
It's a requirement for a project, so if it's not possible, I need to know the reasons (or it's only not available through API?).
If you are using Tomcat, you may be able to create a custom session manager (see this discussion). You would then have access to the Tomcat Session object and could call setId.
The servlet API does not support creating your own cookie value. In fact, it does not guarantee that sessions are maintained via cookies... it specifically states that they can be maintained via a mechanism such as "URL Rewriting". It DOES guarantee that the session is maintained in some fashion, and that pretty much requires some sort of unique ID which is passed to the browser and returned, but no mechanism is provided in the Servlet API for servlet code to control what value is used. Nor do common servlet containers that I know of (such as Tomcat) provide a means for controlling this value.
However, none of this should be a problem for you if I understand your requirements properly. Just because Tomcat (or whatever servlet container you use) is creating a unique ID based on its own algorithms (that contain things like cryptographically secure random number generators to prevent "guessing" of session IDs), doesn't mean that you cannot create a SEPARATE ID which meets your requirements (based on user and time, unique across all sessions... whatever you need). This ID can be stored in the session (if that's all you need), or can be stored on the browser in a separate cookie (if you need it maintained across sessions). The only effect would be that the browser was storing TWO cookies -- and most websites store many more cookies than that!
Um...if you have the code to generate a unique ID, you can just do this:
/**
* The String key of the user id attribute.
*/
public static final String USER_ID_KEY = "userIdKey";
// Set the user attribute (createUniqueUserId's parameters and return type are up to you)
httpSession.setAttribute(USER_ID_KEY, createUniqueUserId());
// Retrieve the user attribute later
httpSession.getAttribute(USER_ID_KEY);
The HttpSession interface also provides a getId() method, which is documented here (copying the documentation for reference):
public java.lang.String getId()
Returns a string containing the unique
identifier assigned to this session.
The identifier is assigned by the
servlet container and is
implementation dependent.
Returns: a
string specifying the identifier
assigned to this session
Related
I've implemented a JAAS LoginModule that is working great with Spring 4 and Struts 2.3. This same LoginModule is also invoked via a ServletFilter in Tomcat 8.0/8.5 to authenticate and authorize requests to Servlets outside of the Spring framework.
The LoginModule uses a simple implementation of java.security.acl.Group and separates the User(s) and Role(s) with two simple implementations of java.security.Principal. By "simple" I mean the minimal implementations that satisfy the interfaces.
The "User" implementation maps the name property to a unique username (actually an e-mail address). Since the e-mail address is unique but could change, the account database contains a unique account identifier (GUID) that is used to assign groups, roles and to log service requests (while also anonymizing our users). In my model, the AccountIdentifier has its own class. Essentially, I have two unique identifiers for accounts, but since the e-mail address needs to be supplied to the LoginModule for authentication, it ended up being the basis for the User principal.
The account identifier is not currently being propagated through to the Subject in the LoginModule, but now I need it in order to log service requests.
I see two ways forward with making the account identifier available via the Subject, but I am uncertain which is the best practice for JAAS:
Extend my current "User" Principal implementation to include an "accountIdentifier" property that is set during LoginModule.commit().
Implement AccountIdentifier as a separate Principal that gets added to the Subject during the LoginModule.commit().
The first option would be easiest, but that also seems like it defeats the purpose of segregating Personally Identifying Information from accounts (which is something I need to do in order to satisfy the upcoming European GDPR requirements).
Should I even be adding the "User" principal (the one that contains the e-mail address) to the Subject?
There are several incompatibilities among the JAAS and Servlet specifications regarding authentication and user principles. Because of this, Spring uses a different approach to JAAS integration than Tomcat.
This answer documents a comprehensive method of implementing JAAS Login Modules in a way that accommodates both Tomcat and Spring.
For clarity, the two options for implementing user principles is copied here from the question:
Extend my current "User" Principal implementation to include an "accountIdentifier" property that is set during LoginModule.commit().
Implement AccountIdentifier as a separate Principal that gets added to the Subject during the LoginModule.commit().
Option 1 has the unfortunate side affect of joining together different forms of Personally Identifiable Information, which in some environments could violate the European GDPR regulations (sessions may be serialized onto disk, and this information would go with it).
Option 2 separates out Personally Identifiable Information, but must be implemented in a way that overcomes several limitations in the Servlet Specification and Tomcat's JAAS implementation.
These limitations are described in detail below, with the bold sections summarizing the main points.
JAASRealm requires that the backing collection(s) of a Subject preserve the ordering of the Principals.
The Tomcat 8.5 JAAS Realm documentation states:
Using JAASRealm gives the developer the ability to combine practically
any conceivable security realm with Tomcat's CMA.
but then goes on to state:
Although not specified in JAAS, you should create separate classes to
distinguish between users and roles, extending javax.security.Principal
so that Tomcat can tell which Principals returned from your login module
are users and which are roles (see org.apache.catalina.realm.JAASRealm).
Regardless, the first Principal returned is always treated as the user
Principal.
Note that the above Tomcat documentation uses the phrase "the user Principal". Although the JAAS API recommends implementing users and roles as distinct classes extending javax.security.Principal, this is not compatible with the Servlet Specification because HttpServletRequest.getUserPrincipal() allows only for a single Principal to be returned:
Returns a javax.security.Principal object containing the name of the
current authenticated user. If the user has not been authenticated,
the method returns null.
A strict reading of the above documentation states that it should contain "...the name of the current authenticated user", but in order to satisfy my original goal, I am interpreting this as "...any name or identifier for the authenticated Subject". This corresponds more closely to the com.sun.security.auth.UserPrincipal documentation (i.e., "A user principal identified by a username or account name").
Due to the above limitations in Tomcat's JAASRealm and the Servlet Specification's HttpServletRequest, it is clearly important that if the account identifier is to be propagated to the request via a ServletFilter (which only has access to the current session, request and response), it must be contained in the first Principal (thus Option 1 in the original question would satisfy this requirement, or Option 2 only if it appears first and I do not need the original username). I believe all I really need is the account identifier, so I am sticking with the second option for now, where I hand an "EmailAddressPrincipal" to MyLoginModule and I receive an "AccountIdentifierPrincipal" back via the Subject (i.e., the MyLogin.commit() adds the "AccountIdentifierPrincipal" as the very first principal).
The JAASRealm documentation is actually slightly contradictory regarding the precise order of Principals, it depends on which section you're reading:
As this Realm iterates over the Principals returned by
Subject.getPrincipals(), it will identify the first Principal that
matches the "user classes" list as the Principal for this user
vs.
Regardless, the first Principal returned is always treated as the user
Principal.
The Servlet API provides no guarantees of the ordering of Principles returned by a Subject.
Essentially, if I were to create a ServletFilter that mimics what JAASRealm is doing, the authentication would look like this (note the iterator in particular):
final LoginContext loginContext = new LoginContext(MyLoginModule.JAAS_REALM, new DefaultCallbackHandler(username, password));
loginContext.login();
final Subject subject = loginContext.getSubject();
request.getSession().setAttribute("AUTH_USER_PRINCIPAL", subject.getPrincipals(AccountIdentifierPrincipal.class).iterator().next());
request.getSession().setAttribute("AUTH_ROLE_PRINCIPALS", subject.getPrincipals(MyRolePrincipal.class));
Unfortunately, this is in direct conflict with the constructor for javax.security.auth.Subject, which mandates that a java.util.Set is used as the backing collection for Principals. Additionally, the Set.iterator() documentation states:
The elements are returned in no particular order (unless this set is
an instance of some class that provides a guarantee).
The earliest access we have to the Subject is in the LoginModule.initialize() method, which is something that is unfortunately invoked somewhere in the internals of LoginContext (I think). This means we have no control over the exact subclass of Set that is used as the backing collection for Principals, and therefore no control over their ordering. By the time this arrives to the ServletFilter, it is a SynchronizedSet, so it isn't even clear what the original class was, or whether re-ordering occurred.
This all indicates that in order for JAASRealm to work as expected only a single user principal can be provided. There is no interface anywhere in that middle layer that clearly establishes the order of the Subject Principals.
Conclusions
When using JAASRealm, only one Principal of the declared User type should be added to the Subject during commit.
When using JAASRealm, avoid using multiple User class names.
Violating the above two rules may lead to undefined and/or inconsistent behavior.
Solution: Use an AuthorityGranter for Spring, and a ServletFilter for non-framework Tomcat servlets
For the sake of Option 2, I'm avoiding the use of JAASRealm because according to all of the above documentation, it does not faithfully adhere to JAAS. This brings me back to the pure ServletFilter approach.
The javax.security.auth.Subject contains everything needed for authorization: multiple user principals, roles and ACL groups. Unfortunately, this class is only partially serializable, which means I can't just wrap the class as a Principal and return it.
In order to satisfy Spring's DefaultJaasAuthenticationProvider, implement an AuthorityGranter to map Principals to role names - that provides complete control over how the mapping is performed.
Since AuthorityGranter isn't available outside the Spring Framework, I also implemented a ServletFilter that uses a similar approach to map roles for my non-Spring webapp. Temporarily, I am using an HttpServletRequestWrapper to read the Principal and Roles from session attributes (stored in session during authentication) and override getUserPrincipal and isUserInRole. Ultimately, I will revisit JAASRealm to see if it contains any functionality for handling this piece, but I'm not quite there yet.
When a user is interacting with a website, there's usually some object that could be considered the "current" object, like a shopping cart, or, to use an example in my world, a manufacturing job. What's the best practice for tracking that current object from page to page? I can think of a few:
put the ID as a request parameter (downside is security, since a
hacker could use that to change the URL to a different ID)
a session object (downside is if you have a huge amount of users
and/or the current object has a large memory footprint)
cookie? (Haven't tried that one)
Have I missed some obvious answer?
BTW, we're using Java, WebLogic and Struts1.
It really depends on your technology stack as to what "best practice" would be for you right now.
For example, if you're building a traditional Rails application using ERB templates without a MVC front end or anything, then I think that using the session object that the Rails framework provides for you would be best.
Most web frameworks will supply you an easy way to keep track of a particular user that's browsing the website. Most of the ones I've seen use a combination of your second and third options. They have a session ID that is stored as a cookie in the user's browser and every time that user sends a request to the web server, it loads a Session object from memory or the database using the session ID value that was stored in that cookie. Memory shouldn't be an issue unless you have an enormous number of users, but that also depends on the type of information that you're storing in there.
If you're storing entire database rows or records, and the sessions are being stored totally in memory, then maybe you should consider only storing the IDs to those records.
Research your particular web framework well to see how it handles user sessions.
As far as a cross-platform best practice, the Session object paradigm seems tot be the best approach to date. It permits matching of request parameters to tracking of sessions independent of the handling of the request parameter. An extension to this is that the Session object provides a handy place to put a collection of things for use if the session is re-identified (Java uses a Map).
Generally the Session is a representation of a single web browser visiting a single website repeatedly. Since there is nothing associating one request to another, this is generated synthetically from a combination of items, including user agent string, reply ip address, etc (in the past it was a stored value in a cookie, which caused no end to security issues).
Assuming you have a reliable Session object available, typically one stores the items that a supposed "single user" driving the session should have access to. So, if you have a shopping cart, it might be represented as a shopping cart object accessible from the session. In Java, that might be a map.
When identifying a new session
// request is a HttpServletRequest object in this example
if (request.getSession(false)) {
// must be a new one
Session newSession = request.getSession(true);
newSession.putValue("shoppingCart", new ShoppingCart());
}
Later on, when adding to the cart
ShoppingCart cart = (ShoppingCart)(request.getSession(false).getValue("shoppingCart"));
cart.addItem(item);
The benefits of separating out the detection from the request handling make it easy to fix / tweak session tracking without altering the code for the "session contents".
The other Answers are correct. Just a bit more explanation.
The Java Servlet technology’s Session object is indeed aimed at solving this exact problem, how to track a single user’s set of current objects over time, beyond the HTTP request-response lifecycle. This lends statefulness to an otherwise stateless protocol (HTTP). Every Servlet container (Tomcat, Jetty, WildFly, and so on) automatically handles the details such as using cookies or URL rewriting so you needn't worry about that.
Yes, as you add stuff to the Session object its memory usage grows in size. If running low on memory, your Servlet container such as Tomcat may choose to write an older Session object to storage using Serialization. The catch here is that all your objects in the Session must be serializable. Ensuring your classes (and all their nested objects) can correctly handle serialization can be a big chore.
Tip: If possible add memory to your server to more that handle your highest session load. As the wise man said, "Algorithms are for people who don’t know how to buy RAM.".
In our web application (in JBoss using Struts) we use sessions largely for security as well as to cache some data for a User. Thus, every user logged into the application has a session and different data cached in it.
Based on some parameter change, i want to change the cache of the subset of users who are logged in (i.e. have session)
Can this be achieved? I have not been able to find anything so far from general search.
You can use a HttpAttributeListener
a basic example here
HttpSessionAttributeListener:
The HttpSessionAttributeListener interface enables an object to
monitor changes to the attribute lists of sessions within a given Web
application. The HttpSessionAttributeListener in turn extends
java.util.EventListener. The methods in it are
attributeAdded(HttpSessionBindingEvent se)- This is the notification that an attribute has been added to a session.
attributeRemoved(HttpSessionBindingEvent se)- This is the notification that an attribute has been removed from a session.
attributeReplaced(HttpSessionBindingEvent se)- This is the notification that an attribute has been replaced in a session.
You can do it by storing each session object in a static List<Session> in some holder object. You can put it by a HttpSessionListener#sessionCreated(..). Remember to remove it from the list on sessionDestroyed(..)
Then, whenever you want to do something, simply loop the previously stored list of sessions and do whatever you want with them.
You have basically 2 options:
Push the changes. Get hold of all HttpSession instances in an application wide map which you manage with help of a HttpSessionListener. This way you can just get them from the application scope and walk through them to make the necessary changes directly.
Poll the changes. Store a change instruction in the application scope. On every HTTP request, check with help of a Filter or ServletRequestListener if a change is required, then make the necessary change in the current session and remove/disable the change instruction.
A completely different alternative is to use an application wide shared cache, such as Terracotta or Ehcache, so that you don't need to duplicate the same data over all HTTP sessions. You'd just need to deal with the data on a per-request basis. When database access comes into picture with JPA, then read on about "2nd level cache", that's exactly what it does.
anyone know if we can create a session-like mechanism in java desktop application?
i am going to develop a java desktop application which has a log-in mechanism to ensure only authorized people can access stored information, then i think if java can something like session, or if there is any mechanism to do something like session. that's all.
can somebody explain?
Basically a session consists of a set of binary or serialized objects, mapped to a session id. Either independently or, most likely, via a Map or Table. So yes, you can implement something like that, by using a smart DB schema. Just be careful about who can see (and use) that DB.
I think, though, your approach is not the best one. After all I think the app will be used by a single user at any time on each machine, so I don't think this is the best approach. Rather you should save the current "session" (AKA app state) in some way, instead of thinking of the session as in the Servlet way.
The session is just a hashmap that is singleton per user . So , you can think that every user has their own hashmap which allows them to store objects. In the servlet , the session is provided by the HttpSession interface.
The session-like mechanism depends on your application 's architecture .If it is a thick client implementation , which contains only 2 tiers (database tier and the java client) ,and most of the business codes are embedded inside the java application , then every user has their own JVM to run the java code on their machines .Thus , you simply create a singleton hashmap for your desktop application .When the application starts , just initialize this hashmap and put a key and its boolean value to indicate the login status of the user ( e.g. singltonHashmap.put ("LOGIN" , false)) .Once the user login , you should update the value of the LOGIN key to true to indicate that the user is logged in .
If the java desktop application is thin client implementation , which has an application server to process all the requests from all users, then you can use the stateful session bean defined by the Enterprise JavaBeans specification (EJB) .There is an one-to-one mapping between a client and a stateful session bean instance .The application server will makes sure that subsequent method invocations by the same client are handled by the same stateful bean instance . You just declare a hashmap inside an stateful session beans and put the objects that you want to keep track during the conversation in that hashmap. Here is an example for your reference.
I am writing a JSR-168 portlet that can be added to a container multiple times. Each container (Liferay, JBoss, etc.) has its own internal way of differentiating between multiple instantiations of the same portlet.
I, however, would like to uniquely identify my portlet instance inside the doView() method itself.
Is there any standard, JSR-168 mechanism to retrieve some unique identifier that's different for each instance of my portlet? I've seen various solutions where people randomly generate unique IDs and save them in the session, but I'd prefer a standard mechanism if one exists.
Portlet 1.0 (168) provides the RenderResponse.getNamespace() method, which should be unique per portlet instance.
From spec: PLT.12.3.4 Namespace encoding:
The getNamespace method must provide
the portlet with a mechanism that
ensures the uniqueness of the returned
string in the whole portal page. For
example, the getNamespace method would
return a unique string that could be
prefixed to a JavaScript variable name
within the content generated by the
portlet, ensuring its 5 uniqueness in
the whole page. The getNamespace
method must return the same value if
invoked multiple times within a render
request.
If you want to access it in processAction, you'll probably want to store it in the session or as an actionURL parameter.
If upgrading is an option, Portlet 2.0 (286) changes the underlying PortletResponse interface to provide the getNamespace() method and also adds a PortletRequest.getWindowID() method which might be of some use to you.
No, there is no common ID for the instance. I have implemented a portlet container myself, there is no per instance id in the public api - the container has one, of cause. The portlet session (javax.portlet.PortletRequest#getPortletSession()) is unique for one portlet (definition by tag in portlet.xml) and one user (javax.servlet.http.HttpSession), that is not enough for you.
So imho an id generated (can also be a simple (sync) counter in the portletl class) and stored in the portlet session is the only portable way. THe portlet class itself is typically shared beween instances, so the java.lang.System#identityHashCode(Object x) is also useless.
Why do you need it?
I am surprised that this unique ID does not seem to exist as per Ame. The Instance ID can be used for storing all the portlet preferences with in our own database rather than the container provided one. One of the reason we need to store this on our own is the preferences provided by container does not support locale specific preferences.
i.e one portlet instance may have different preferences per locale.
We are trying to use Liferay for our needs.