I am experimenting with setting the cookie path in my application's web.xml (as suggested here) to:
<session-config>
<cookie-config>
<path>/</path>
</cookie-config>
</session-config>
So I deploy two identical web applications to localhost:8080/application-a and localhost:8080/application-b respectively.
Each application is a single servlet:
public class ControllerServlet extends HttpServlet{
#Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
HttpSession session = req.getSession(false);
if (session == null) {
session = req.getSession(true);
System.out.printf("No session was present - new one created with JSESSIONID=[%s]\n", session.getId());
} else {
System.out.printf("JSESSIONID cookie was present and HttpSession objects exists with JSESSIONID=[%s]\n", session.getId());
}
}
}
I deploy the apps to a Tomcat 8.5 container (tried with Tomcat 9 as well the behavior is the same). When I visit with my browser the application-a, here's what I see:
… and on the Tomcat logs I read:
No session was present - new one created with JSESSIONID=[A227B147A4027B7C37D31A4A62104DA9]
So far so good. When I then visit application-b here's what I see:
… and the Tomcat logs show:
No session was present - new one created with JSESSIONID=[5DC8554459233F726628875E22D57AD5]
This is also very well as explained here and also in this answer and I quote:
SRV.7.3 Session Scope
HttpSession objects must be scoped at the application (or servlet
context) level. The underlying mechanism, such as the cookie used to
establish the session, can be the same for different contexts, but the
object referenced, including the attributes in that object, must never
be shared between contexts by the container.
So even though on the request the JSESSIONID cookie was present, my application (the one deployed in application-b) was unable to find an HttpSession object in its own servlet context scope and so a new session object was created and a new value was assigned to the JSESSIONID cookie.
However, when I now go back to my application-a I find out that because of the / value configured for the cookie path, it is now trying to use the JSESSIONID value set by application-b and of course its servlet doesn't find such a session object in its own context (application-a) and so a new value for the JSESSIONID cookie is created which will in turn invalidate the session of the application-b application and so on and so forth ad infinitum as I switch back and forth between the two applications.
So my questions are:
1 given the above behavior it would seem impossible for two applications to use the same JSESSIONID cookie value as the key to their respective HttpSession objects. So in fact not only are the HttpSession objects always different and scoped at the application (servlet context) level but also, in practice, the JSESSIONID values have to be different. Is that correct?
2 If so, then why does the servlet specification use the wording:
The underlying mechanism, such as the cookie used to establish the
session, can be the same for different contexts [...]
The only way I can imagine the above could be accomplished would be to have a way to hardcodedly provide the JSESSIONID value to use when a new session object is created? But I don't see an API for that.
3 Is there a way I can have some other cookies be shared among applications using the / path in the <session-config> XML element but not have the / path apply to the JSESSIONID cookie? In other words does the <session-config> apply to all cookies of an application or only the cookie used for session tracking? (JSESSIONID) ?
Upon further experimentation and taking a cue from this answer it would appear that for the same JSESSIONID to be used for all web applications it is necessary to set the following attribute in context.xml:
<Context ... sessionCookiePath="/">
Either the Tomcat-wide context.xml or the WAR-specific context.xml will do. The <cookie-config><path> value configured in the WAR's web.xml is apparently ignored.
Regarding point 3 of my question I 've found that the way to set paths for other cookies is to programmatically create many of them, one for each path, and add them in the response object with the addCookie method. The configurations in web.xml or context.xml are appicable to other cookies beyond the session cookie.
Related
How can i dynamically change the domain of the JSESSIONID cookie that tomcat or jetty generates?
I stumbled accross this one:
Sharing session data between contexts in Tomcat
But i need to do this on request basis (the above is on context basis).
For example:
request1 comes from www.testdomain1.com and needs the domain ".testdomain1.com".
request2 comes from www.testdomain2.com and needs the domain ".testdomain2.com".
The domains are not known at deploy time and can change any time.
I used a simple servlet filter with a HttpServletResponseWrapper for cookies from my application, but JSESSIONID doesn't get intercepted by the filter (it's not generated by the webapp but by the container).
[edit] can't go the httpRequest.getSession().getServletContext().getSessionCookieConfig().setDomain() way, cause it seems the sessioncookieconfig is readonly after the context is initialized..
[edit] can't go with a tomcat valve cause the response is already commited (isCommitted is true). In addition tomcat doesn't use reponse addCookie or header methodes to set JSESSIONID cookie. And Response class is final so not wrapper can't be created for that one.
[edit] with jetty a custom SessionHandler is not working either, cause there is no access to the request object.
I highly suspect this entire thing is impossible. The only solution i can see is to put a proxy in front of the servlet container and change the cookie this way, but that is overkill.
In the next request that is after the session is created, get all the cookies
Cookie[] c = request.getCookies();
//for loop{
if(c.getName.equals("JSESSIONID")){
c.setDomain("....);
}
}
How can I pass the session attribute value to another application which is in same web server. The reason why I should know this is because we have a project which is divide by a module. Each module will redirect to another that pass value using session. That session will be used to identify which user is accessing that module.
Suppose I have a LogIn Module that separate from my other module.
Here my sample code:
Sample Url http://localhost:8080/Login
authorization.jsp : This page will call after user input a userId and then submit
page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"
HttpSession sessionid = request.getSession();
String userId = request.getParameter("userId");
sessionid.setAttribute("userId", userId);
System.out.println("SESSION IS :" + sessionid.getAttribute("userId"));
response.sendRedirect("http://localhost:8080/OtherModule");
Sample Url http://localhost:8080/OtherModule
In my Home servlet I will check if the session have a userId
protected void doGet(HttpServletRequest request, HttpServletResponse response){
HttpSession session = request.getSession();
if(session.getAttribute("userId") != null){
System.out.println("SESSION ID # GET: " + session.getAttribute("userId"));
} else {
System.out.println("No Session for userId");
}
}
//I also tried this with post but still I can't get the session
I hope this information may give you idea what's wrong with my code. Please help me with this. Thanks
You will have to configure your web-server accordingly. Tomcat for example provides a valve for that. See here: http://tomcat.apache.org/tomcat-6.0-doc/config/host.html#Single_Sign_On
Note: The localhost-URL you posted, only works on your computer (hence the name "local").
It would be much easier though to just add all of your modules into one Web-Application or to use one of the countless Java Web Application Frameworks.
If your shared data is related to authentication/login, then SSO (single sign on) is the way to go as #Ridcully says - it's managed for you by the application server, and your apps shouldn't need to worry about it.
If the problem is more general - how to share data between webapps - then a very clean approach is to use JNDI. Tomcat (and any other servlet/J2EE server) provide a lookup space that can be common to all webapps, this mechanism is most often used to define database (or other resource) configuration outside apps in a way that can be shared.
So, you could write a class to contain the data, have it in JNDI, and have each application look it up (using explicit JNDI calls or resource injection). If you need more info on this let me know.
Single sign on indeed solves the shared-logged-in-user issue.
It does however not allow for sharing the same session among all deployed webapplications. If that is after all your actual intent, then you need to set emptySessionPath attribute of the <Connector> element in /conf/server.xml to true.
<Connector ... emptySessionPath="true">
See also Tomcat 6.0 HTTP Connector documentation.
This question already has answers here:
How do servlets work? Instantiation, sessions, shared variables and multithreading
(8 answers)
Closed 5 years ago.
So far I understand Httpsession concepts in Java.
HttpSession ses = req.getSession(true);
will create a session object, according to the request.
setAttribute("String", object);
will, bind the 'String', and value with the Session object.
getAttribute("String");
will return an object associated with the string, specified.
What I am not able to understand is: I am creating a session object like
HttpSession ses = req.getSession(true);
and setting a name for it by calling setAttribute("String", object);.
Here, This code resides inside the server. For every person, when he tries to login the same code in the server will be executed. setAttribute("String", object); in this method the string value is a constant one. So, each session object created will be binded by the same string which I have provided. When I try to retrieve the string to validate his session or while logout action taken the getAttribute("String"); ll return the same constant string value(Am I right!!?? Actually I don't know, I'm just thinking of its logic of execution). Then, how can I be able to invalidate.
I saw this type of illustration in all of the tutorials on the WEB. Is it the actual way to set that attribute? Or, real application developers will give a variable in the "String" field to set it dynamically
(ie. session.setAttribut(userName, userName); //Setting the String Dynamically.. I dono is it right or not.)
And my final question is
WebContext ctx = WebContextFactory.get();
request = ctx.getHttpServletRequest();
What do the two lines above do? What will be stored in ctx & request?
HttpSession ses = req.getSession(true); will creates new session means. What value stored in ses.
Some [random] precisions:
You don't need login/logout mechanisms in order to have sessions.
In java servlets, HTTP sessions are tracked using two mechanisms, HTTP cookie (the most commonly used) or URL rewriting (to support browsers without cookies or with cookies disabled). Using only cookies is simple, you don't have to do anything special. For URL re-writing, you need to modify all URLs pointing back to your servlets/filters.
Each time you call request.getSession(true), the HttpRequest object will be inspected in order to find a session ID encoded either in a cookie OR/AND in the URL path parameter (what's following a semi-colon). If the session ID cannot be found, a new session will be created by the servlet container (i.e. the server).
The session ID is added to the response as a Cookie. If you want to support URL re-writing also, the links in your HTML documents should be modified using the response.encodeURL() method. Calling request.getSession(false) or simply request.getSession() will return null in the event the session ID is not found or the session ID refers to an invalid session.
There is a single HTTP session by visit, as Java session cookies are not stored permanently in the browser. So sessions object are not shared between clients. Each user has his own private session.
Sessions are destroyed automatically if not used for a given time. The time-out value can be configured in the web.xml file.
A given session can be explicitly invalidated using the invalidate() method.
When people are talking about JSESSIONID, they are referring to the standard name of the HTTP cookie used to do session-tracking in Java.
I suggest you read a tutorial on Java sessions. Each user gets a different HttpSession object, based on a JSESSIONID request/response parameter that the Java web server sends to the browser. So every user can have an attribute with the same name, and the value stored for this attribute will be different for all users.
Also, WebContextFactory and WebContext are DWR classes that provide an easy way to get the servlet parameters.
As I understand it, your concerns are about separation of the different users when storing things in the HttpSession.
The servlet container (for example Tomcat) takes care of this utilizing its JSESSIONID.
The story goes like this :
User first logs onto website.
Servlet container sets a COOKIE on
the user's browser, storing a UNIQUE
jsessionId.
Every time the user hits the
website, the JSESSIONID cookie is
sent back.
The servlet container uses this to
keep track of who is who.
Likewise, this is how it keeps track
of the separation of data. Every
user has their own bucket of
objects uniquely identified by the
JSESSIONID.
Hopefully that (at least partially) answers your question.
Cheers
Your basic servlet is going to look like
public class MyServlet{
public doGet(HttpServletRequest req, HttpServletResponse res){
//Parameter true:
// create session if one does not exist. session should never be null
//Parameter false:
// return null if there is no session, used on pages where you want to
// force a user to already have a session or be logged in
//only need to use one of the two getSession() options here.
//Just showing both for this test
HttpSession sess = req.getSession(true);
HttpSession sess2 = req.getSession(false);
//set an Attribute in the request. This can be used to pass new values
//to a forward or to a JSP
req.setAttribute("myVar", "Hello World");
}
}
There is no need to set any attribute names for your session that is already done. As others have suggested in other answers, use cookies or URL re-writing to store the sessionID for you.
When you are dealing with the DWR WebContext, it is simply doing the same thing as above, just normally the Request object isn't passed into the method, so you use the WebContext to get that request for you
public class DWRClass {
public doSomething(){
WebContext ctx = WebContextFactory.get();
HttpServletRequest req = ctx.getHttpServletRequest();
HttpSession sess = req.getSession(); //no parameter is the same as passing true
//Lets set another attribute for a forward or JSP to use
ArrayList<Boolean> flags = new ArrayList<Boolean>();
req.setAttribute("listOfNames", flags);
}
}
I would like to eliminate the HttpSession completely - can I do this in web.xml? I'm sure there are container specific ways to do it (which is what crowds the search results when I do a Google search).
P.S. Is this a bad idea? I prefer to completely disable things until I actually need them.
I would like to eliminate the HttpSession completely
You can't entirely disable it. All you need to do is to just not to get a handle of it by either request.getSession() or request.getSession(true) anywhere in your webapplication's code and making sure that your JSPs don't implicitly do that by setting <%#page session="false"%>.
If your main concern is actually disabling the cookie which is been used behind the scenes of HttpSession, then you can in Java EE 5 / Servlet 2.5 only do so in the server-specific webapp configuration. In for example Tomcat you can set the cookies attribute to false in <Context> element.
<Context cookies="false">
Also see this Tomcat specific documentation. This way the session won't be retained in the subsequent requests which aren't URL-rewritten --only whenever you grab it from the request for some reason. After all, if you don't need it, just don't grab it, then it won't be created/retained at all.
Or, if you're already on Java EE 6 / Servlet 3.0 or newer, and really want to do it via web.xml, then you can use the new <cookie-config> element in web.xml as follows to zero-out the max age:
<session-config>
<session-timeout>1</session-timeout>
<cookie-config>
<max-age>0</max-age>
</cookie-config>
</session-config>
If you want to hardcode in your webapplication so that getSession() never returns a HttpSession (or an "empty" HttpSession), then you'll need to create a filter listening on an url-pattern of /* which replaces the HttpServletRequest with a HttpServletRequestWrapper implementation which returns on all getSession() methods null, or a dummy custom HttpSession implementation which does nothing, or even throws UnsupportedOperationException.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) request) {
#Override
public HttpSession getSession() {
return null;
}
#Override
public HttpSession getSession(boolean create) {
return null;
}
}, response);
}
P.S. Is this a bad idea? I prefer to completely disable things until I actually need them.
If you don't need them, just don't use them. That's all. Really :)
If you are building a stateless high load application you can disable using cookies for session tracking like this (non-intrusive, probably container-agnostic):
<session-config>
<tracking-mode>URL</tracking-mode>
</session-config>
To enforce this architectural decision write something like this:
public class PreventSessionListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent se) {
throw new IllegalStateException("Session use is forbidden");
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
throw new IllegalStateException("Session use is forbidden");
}
}
And add it to web.xml and fix places where it fails with that exception:
<listener>
<listener-class>com.ideas.bucketlist.web.PreventSessionListener</listener-class>
</listener>
In Spring Security 3 with Java Config, you can use HttpSecurity.sessionManagement():
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
Xml looks like this;
<http create-session="stateless">
<!-- config -->
</http>
By the way, the difference between NEVER and STATELESS
NEVER:Spring Security will never create an HttpSession, but will use
the HttpSession if it already exists
STATELESS:Spring Security will never create an HttpSession and it will
never use it to obtain the SecurityContext
I use the following method for my RESTful app to remove any inadvertent session cookies from being created and used.
<session-config>
<session-timeout>1</session-timeout>
<cookie-config>
<max-age>0</max-age>
</cookie-config>
</session-config>
However, this does not turn off HttpSessions altogether. A session may still be created by the application inadvertently, even if it disappears in a minute and a rogue client may ignore the max-age request for the cookie as well.
The advantage of this approach is you don't need to change your application, just web.xml. I would recommend you create an HttpSessionListener that will log when a session is created or destroyed so you can track when it occurs.
I would like to eliminate the HttpSession completely - can I do this in web.xml? I'm sure there are container specific ways to do it
I don't think so. Disabling the HttpSession would be a violation of the Servlet spec which states that HttpServletRequest#getSession should return a session or create one. So I wouldn't expect a Java EE container to provide such a configuration option (that would make it non compliant).
Is this a bad idea? I prefer to completely disable things until I actually need them.
Well, I don't really get the point, just don't put anything in the session if you don't want to use it. Now, if you really want to prevent the use of the session, you can use a Filter to replace the request with a implementation of HttpServletRequestWrapper overriding getSession(). But I wouldn't waste time implementing this :)
Update: My initial suggestion was not optimal, the "right" (cough) way would be to replace the request.
As of Servlet 3.0, you can make it so sessions are not tracked by the servlet container in any way, by adding code like this to the contextInitialized method of a ServletContextListener:
servletContext.setSessionTrackingModes(Collections.emptySet());
Javadoc.
Rather than disabling you can rewrite the URL using a URL rewrite filter eg tuckey rewrite filter. This will give Google friendly results but still allow cookie based session handling.
However, you should probably disable it for all responses as it's worse than just search engine unfriendly. It exposes the session ID which can be used for certain security exploits.
Example config for Tuckey filter:
<outbound-rule encodefirst="true">
<name>Strip URL Session ID's</name>
<from>^(.*?)(?:\;jsessionid=[^\?#]*)?(\?[^#]*)?(#.*)?$</from>
<to>$1$2$3</to>
</outbound-rule>
One cannot avoid the session creation. But you can check if you violate your own requirement at the end of a request cycle. So, create a simple servlet filter, which you place as first and after chain.doFilter throw an exception if a session was created:
chain.doFilter(request, response);
if(request.getSession(false) != null)
throw new RuntimeException("Somewhere request.getSession() was called");
For RESTful application, I simply invalidate it every time the request's lifecycle ends. There may be some web server that always creates new session when new client access whether you call request.getSession() or not.
I would like to know if there is some way to share a variable or an object between two or more Servlets, I mean some "standard" way. I suppose that this is not a good practice but is a easier way to build a prototype.
I don't know if it depends on the technologies used, but I'll use Tomcat 5.5
I want to share a Vector of objects of a simple class (just public attributes, strings, ints, etc). My intention is to have a static data like in a DB, obviously it will be lost when the Tomcat is stopped. (it's just for Testing)
I think what you're looking for here is request, session or application data.
In a servlet you can add an object as an attribute to the request object, session object or servlet context object:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String shared = "shared";
request.setAttribute("sharedId", shared); // add to request
request.getSession().setAttribute("sharedId", shared); // add to session
this.getServletConfig().getServletContext().setAttribute("sharedId", shared); // add to application context
request.getRequestDispatcher("/URLofOtherServlet").forward(request, response);
}
If you put it in the request object it will be available to the servlet that is forwarded to until the request is finished:
request.getAttribute("sharedId");
If you put it in the session it will be available to all the servlets going forward but the value will be tied to the user:
request.getSession().getAttribute("sharedId");
Until the session expires based on inactivity from the user.
Is reset by you:
request.getSession().invalidate();
Or one servlet removes it from scope:
request.getSession().removeAttribute("sharedId");
If you put it in the servlet context it will be available while the application is running:
this.getServletConfig().getServletContext().getAttribute("sharedId");
Until you remove it:
this.getServletConfig().getServletContext().removeAttribute("sharedId");
Put it in one of the 3 different scopes.
request - lasts life of request
session - lasts life of user's session
application - lasts until applciation is shut down
You can access all of these scopes via the HttpServletRequest variable that is passed in to the methods that extend from the HttpServlet class
Depends on the scope of the intended use of the data.
If the data is only used on a per-user basis, like user login info, page hit count, etc. use the session object
(httpServletRequest.getSession().get/setAttribute(String [,Object]))
If it is the same data across multiple users (total web page hits, worker threads, etc) use the ServletContext attributes. servlet.getServletCongfig().getServletContext().get/setAttribute(String [,Object])). This will only work within the same war file/web applicaiton. Note that this data is not persisted across restarts either.
Another option, share data betwheen contexts...
share-data-between-servlets-on-tomcat
<Context path="/myApp1" docBase="myApp1" crossContext="true"/>
<Context path="/myApp2" docBase="myApp2" crossContext="true"/>
On myApp1:
ServletContext sc = getServletContext();
sc.setAttribute("attribute", "value");
On myApp2:
ServletContext sc = getServletContext("/myApp1");
String anwser = (String)sc.getAttribute("attribute");
Couldn't you just put the object in the HttpSession and then refer to it by its attribute name in each of the servlets?
e.g:
getSession().setAttribute("thing", object);
...then in another servlet:
Object obj = getSession.getAttribute("thing");
Here's how I do this with Jetty.
https://stackoverflow.com/a/46968645/1287091
Uses the server context, where a singleton is written to during startup of an embedded Jetty server and shared among all webapps for the life of the server. Can also be used to share objects/data between webapps assuming there is only one writer to the context - otherwise you need to be mindful of concurrency.