In Web Server programming, session state management is an important issue that must be considered frequently. This article analyzes the session management mechanism of JSP/Servlet and the problems it faces, and then proposes an improved session management method.
1. servlet session management mechanism
HTTP is designed as a stateless protocol. It means that the Web application does not know the information about the previous requests of the same user. One way to maintain session status information is to use the session tracking function provided by the servlet or JSP Container. The servlet API Specification defines a simple httpsession interface, through which we can easily implement session tracking.
The httpsession interface provides methods for storing and returning standard session attributes. Standard session attributes, such as session identifiers and application data, are saved as "name-value" pairs. In short, the httpsession interface provides a standard method to save objects to the memory and extract these objects in the same user's subsequent requests. The method for saving data in a session is setattribute (string S, object O). The method for extracting the originally saved object from the session is getattribute (string S ).
In HTTP, there is no explicit termination signal when the user is no longer active. For this reason, we do not know whether the user has to return again. If we do not solve this problem in some way, a large number of httpsession objects will be accumulated in the memory.
To this end, Servlet uses the "timeout limit" method to determine whether the user is still accessing: if a user does not send a subsequent request within a certain period of time, the user's session will be voided, the httpsession object is released. The default session timeout interval is defined by the servlet container. This value can be obtained through the getmaxinactiveinterval method and modified using the setmaxinactiveinterval method. The timeout time in these methods is measured in seconds. If the Session Timeout value is set to-1, the session never times out. Servlet can use the getlastaccessedtime method to obtain the last access time before the current request.
To obtain the httpsession object, we can call the getsession method of the httpservletrequest object. To correctly maintain the session status, we must call the getsession method before sending any response content.
User sessions can be voided manually or automatically. An expired session means that the httpsession object and its data are deleted from the memory. For example, if a user no longer sends a request within a certain period of time (30 minutes by default), the Java Web server will automatically invalidate his session.
The Servlet/jsp session tracking mechanism has certain limitations, such:
· Session objects are stored in the memory, occupying considerable resources.
· Session Tracing depends on cookies. For various reasons, especially for security reasons, some users disable cookies.
· Session tracing uses the session identifier created by the server. In multiple web servers and multiple JVM environments, the web server cannot identify the session identifiers created by other servers, and the session tracking mechanism cannot play a role.
To thoroughly understand the session tracing mechanism, we must first understand how sessions work in Servlet/jsp containers.
Ii. Session identifier
Each time a new user requests a JSP page that uses an httpsession object, the JSP Container sends a special number to the browser in addition to sending back the response page. This special number is called a session identifier, which is a unique user identifier. After that, the httpsession object will reside in the memory and wait for the same user to return its method again.
On the client, the browser saves the session identifier and sends it to the server in each subsequent request. The session identifier tells the JSP Container that the current request is not the first request sent by the user. The server has previously created an httpsession object for this user. In this case, the JSP Container does not create a new httpsession object for the user, but searches for an httpsession object with the same session identifier, and then establishes the association between the httpsession object and the current request.
The session identifier is transmitted between the server and the browser in the form of a cookie. What if the browser does not support cookies? In this case, subsequent requests to the server do not contain session identifiers. As a result, the JSP Container considers the request to come from a new user and creates another httpsession object. The previously created httpsession object still resides in the memory, however, the user's previous session information is lost.
In addition, the servlet/JSP Container only recognizes the session identifier it creates. If the same web application runs on multiple servers of the Web farm, such a mechanism must exist: ensure that requests from the same user are always directed to the server that processes the user's first request.
Iii. Pseudo-session management mechanism
As mentioned above, cookie-based session management technology faces various problems. Next we will design a new session management mechanism to solve these problems. This session management mechanism is called the "pseudo session" mechanism, which has the following features:
· Objects and data are not stored in memory, but saved as text files. Each text file is associated with a specific user. The file name is the session identifier. Therefore, the file name must be unique.
· Text files are stored in a dedicated Directory, which can be accessed by all web servers. Therefore, pseudo sessions can be used on Web farms.
· The session identifier is directly encoded into the URL instead of being sent as a cookie. Therefore, the use of pseudo-session technology requires that all hyperlinks, including the action attribute of HTML forms, be modified.
In addition, we need to consider the following points when implementing the pseudo-session management mechanism:
· It should be unrelated to the application. Other developers who want to implement the same function should be able to reuse it conveniently.
· For security reasons, there should be a way to generate random numbers for session identifiers.
· Set a timeout value to invalidate expired sessions. If a user returns again after a certain period of time, the user will obtain a new session identifier. This prevents unauthorized users from using others' sessions.
· There should be a mechanism to collect expired sessions and delete the corresponding text files.
· If the user uses an expired session identifier to access the server again, even if the text file of the session identifier has not been deleted, the system should not allow the user to use the original session.
· At the same time, there should be a mechanism to update the last modification time of the session text file, so that the session is always up-to-date and valid when the user returns before the session expiration time.
Iv. Implement the pseudo-session management mechanism
The project described below is called the pseudo Session, which is a simple implementation of the pseudo session mechanism. Considering portability, we implement it in the form of JavaBean. The complete code of the pseudo sessionbean can be downloaded from the end of this article.
Pseudo sessionbean has the following fields ):
Public String path; public long timeout;
PATH is the directory that stores all session text files. If the number of web servers is greater than one, this directory must be accessible to all servers. However, in order to prevent users from directly accessing these text files, this path should not allow users to access them directly. One way to solve this problem is to use directories outside the Web site root.
Timeout is the time between the user's last request and the expiration time of the session. In the pseudo sessionbean code list, timeout is set to 20 minutes in milliseconds, which is a reasonable timeout value. If a user sends a request after the timeout period, the user will get a new session identifier.
Pseudo sessionbean has four methods: getsessionid, setvalue, getvalue, and deleteallinvalidsessions.
4.1 getsessionid Method
The getsessionid method is declared as follows:
Public String getsessionid (httpservletrequest request)
This method should be called at the beginning of each JSP page. It completes the following tasks:
· If the user is visiting for the first time, a new session identifier is set for the user.
· Check the validity of the session identifier contained in the URL. If the session ID has expired, the getsessionid method returns a new session ID.
Let's take a look at the working process of the getsessionid method.
String sessionid = request. getparameter ("sessionid ");
Validsessionidfound is a tag used to indicate whether the session identifier is legal. The initial value of validsessionidfound is false.
Boolean validsessionidfound = false;
The now variable of the long type contains the server time when the request appears. This variable is used to determine the validity of a user session.
Long Now = system. currenttimemillis ();
If the session identifier is found, the getsessionid method checks its validity. The check process is as follows:
· A Valid Session identifier must have a text file of the same name.
· The last modification time and timeout of the file must be later than the current time.
· If a text file corresponding to the session exists but the file has expired, the original file will be deleted.
· Change the last modification date of the text file corresponding to the legal session identifier to now.
These tasks are mainly completed by using the file object. The parameters for creating the file object are the path of the session text file:
If (sessionid! = NULL) {file F = new file (path + sessionid); If (F. exists () {If (F. lastmodified () + timeout> now) {// The session is valid // when setlastmodified is used, if the file has been locked by another program, // the program will not produce any exceptions, but the file data will not change F. setlastmodified (now); validsessionidfound = true;} else {// The session has expired // delete the file F. delete () ;}// end if (F. exists)} // end if (sessionid! = NULL)
If there is no valid session identifier, the getsessionid method generates a session identifier and the corresponding text file:
If (! Validsessionidfound) {sessionid = long. tostring (now); // create a file F = new file (path + sessionid); try {f. createnewfile ();} catch (ioexception IOE) {}} // end of if! Validsessionidfound
The method for programs to ensure random file names is very simple: directly convert the current system time into session identifiers. For applications involving sensitive data, we should consider using a safer random number generator to generate session identifiers.
To sum up, getsessionid does not always return a New Valid Session identifier: the identifier returned by getsessionid may be the same as the identifier passed to it or the newly created session identifier.
To ensure that the JSP page has a valid session identifier to call the setvalue and getvalue methods, each JSP page must call the getsesstionid method at the beginning.
4.2 setvalue Method
The setvalue method saves the value string and the name of the string associated with it. This "name-value" pair can easily remind people of a dictionary object. The setvalue method requires that a valid session identifier be provided in the first parameter. It assumes that the getsessionid method has been executed before it is called, and the verified legal session identifier must exist, therefore, it no longer checks the validity of incoming session identifiers.
The setvalue method saves the name-Value Pair according to the following rules:
· If the name associated with the value has not been saved before, add the new name-value pair to the end of the text file.
· If the name value associated with the value string has been saved before, the original value is replaced by the new value.
The setvalue method saves the name-value pair in the following format. Note that the "name" is case sensitive:
Name-1 value-1name-2 value-2name-3 value-3... name-N value-n
The setvalue method is declared as follows:
Public void setvalue (string sessionid, string name, string value)
The setvalue method first looks for the text file corresponding to the current session. If the text file cannot be found, the setvalue method will not return anything directly. If the session text file is found, the setvalue method reads each row of the text file and then compares the read rows with the name. If the read text line starts with the name, the name is saved, and the setvalue method replaces the value after the row. If the name cannot match the read text line, this line of text is directly copied to a temporary file.
The implementation code of this function is as follows:
Try {filereader Fr = new filereader (path + sessionid); bufferedreader BR = new bufferedreader (FR); filewriter fw = new filewriter (path + sessionid + ". TMP "); bufferedwriter BW = new bufferedwriter (FW); string s; while (S = BR. readline ())! = NULL) if (! S. startswith (name + "") {BW. write (s); BW. newline ();} BW. write (name + "" + value); BW. newline (); BW. close (); BR. close (); FW. close (); BW. close ();...} catch (filenotfoundexception e) {} catch (ioexception e) {system. out. println (E. tostring ());}
After all rows in the original text file are copied to the temporary file, the setvalue method deletes the original text file and changes the temporary file to the name of the session text file:
File F = new file (path + sessionid + ". tmp"); file DEST = new file (path + sessionid); DeST. Delete (); F. renameto (DEST );
4.3 getvalue Method
The getvalue method is used to extract data originally stored in a pseudo-session. Just like the setvalue method, the getvalue method also requires a valid session identifier, and the getvalue method does not check the validity of the passed session identifier. The second parameter of the getvalue method is the name of the data to be extracted. The returned value is the value associated with the specified name.
The getvalue method is declared as follows:
Public String getvalue (string sessionid, string name)
The basic execution process of the getvalue method is as follows: first find the session text file, then read it by line until the text line that matches the name is found; after finding the matched text line, the getvalue method returns the value stored in the row. If the value cannot be found, the getvalue method returns NULL.
4.4 deleteallinvalidsessions Method
The deleteallinvalidsessions method deletes text files associated with expired sessions. The deleteallinvalidsessions method is not a key method because expired session text files are deleted when the getsessionid method is called. The application determines when to call this method. For example, we can write a dedicated background program that clears all expired text files once a day. The simplest way is to call the deleteallinvalidsessions method at the end of the JSP file. However, if the website is busy, repeatedly calling the deleteallinvalidsessions method will reduce the response capability of the entire website. A wise way is to write a background program that automatically cleans up traffic when traffic is low.
The deleteallinvalidsessions method is declared as follows:
Public void deleteallinvalidsessions ()
It first reads the names of all session text files into the files string array:
File dir = new file (PATH); string [] files = dir. List ();
The deleteallinvalidsessions method compares the last modification time (with the timeout time) of the text file with the current system time to determine whether the session has expired. The long variable now is used to save the current time of the system.
Long Now = system. currenttimemillis ();
Next, the deleteallinvalidsessions method cyclically accesses the files array and checks the lastmodified attribute of each file in sequence. All files associated with expired sessions will be deleted:
For (INT I = 0; I <files. length; I ++) {file F = new file (path + files [I]); If (F. lastmodified () + timeout> now) F. delete (); // Delete expired session text files}
V. Application Instances
After compiling the pseuan pseudo-sessionbean, we can use the pseudo-session management mechanism to manage the session status information of Web applications. Because you no longer need to use the server's session management mechanism, you can set the session attribute to false in the page command to disable the default JSP/servlet session management function.
<% @ Page session = "false" %>
Then, we use the <JSP: usebean> tag of JSP to tell the JSP Container program to use the pseudo-sessionbean:
<JSP: usebean id = "pseudo dosessionid" Scope = "application" class = "pseudo dosession. pseudo dosessionbean"/>
In the preceding <JSP: usebean> tag, the class property value is in the form of "package. Class Name. Of course, for different package names, the class attribute value should be modified accordingly. Note that the scope attribute of bean is "application" because we need to use this bean on all pages of the application. In this application, setting the scope attribute of bean to "application" has the best efficiency, because we only need to create bean objects once. In addition, as mentioned above, the getsessionid method must be called before all other code.
<% String sessionid = pseudo dosessionid. getsessionid (request); %>
To illustrate the application of pseudo sessionbean, we will look at two JSP pages: index. jsp and secondpage. jsp. The index. jsp page saves the user name in the pseudo-session variable, while secondpage. jsp extracts the username.
The code for the index. jsp page is as follows:
<% @ Page session = "false" contenttype = "text/html; charset = gb2312" %>
<JSP: usebean id = "pseudo dosessionid" Scope = "application" class = "pseudo dosession. pseudo dosessionbean"/>
<% String sessionid = pseudo dosessionid. getsessionid (request); %>
<HTML>
<Head>
<Title> pseudo-session </title>
</Head>
<Body>
<H1> pseudo-session management mechanism <% String username = "Bulbul"; pseudo sessionid. setvalue (sessionid, "username", username); %>
<A href = secondpage. jsp? Sessionid = <% = sessionid %> click here </a>
<Form method = "Post" Action = anotherpage. jsp? Sessionid = <% = sessionid %>
Input data: <input type = "text" name = "sample">
<Input type = "Submit" name = "Submit" value = "Submit">
</Form>
</Body>
</Html>
<% Pseudo sessionid. deleteallinvalidsessions (); %>
Note: All hyperlinks, including the action attribute marked by <form>, have been rewritten and now contain session identifiers. Note that the deleteallinvalidsessions method is called at the end of the page.
The secondpage. jsp page simply returns the previously saved username.
<% @ Contenttype = "text/html; charset = gb2312" Page session = "false" %>
<JSP: usebean id = "pseudo dosessionid" Scope = "application" class = "pseudo dosession. pseudo dosessionbean"/>
<% String sessionid = pseudo dosessionid. getsessionid (request); %>
<HTML>
<Head>
<Title> 2nd pages </title>
</Head>
<Body>
<% String username = pseudo dosessionid. getvalue (sessionid, "username"); Out. println ("username =" + username); %>
</Body>
</Html>