Correctly and gracefully resolve user exit--jsp and Struts Solutions

Source: Internet
Author: User
Tags abstract date define exit header html page stmt valid
js| Solution Summary

In a password-protected Web application, it is not just a matter of calling the HttpSession invalidate () method to properly handle the user exit process. Now most browsers have back and forward buttons that allow the user to step back or forward to a page. If the user presses the Back button browser to present the cached page to the user after exiting a Web application, this can cause confusion to the user, who will begin to worry about the safety of their personal data. Many Web applications force users to shut down the entire browser when they exit, so users cannot click the Back button. Others use JavaScript, but in some client browsers this does not necessarily work. These solutions are clumsy and are not guaranteed to be 100% effective in either case, and it also requires a certain amount of operational experience from the user.
This article illustrates the correct solution to the user exit problem. The author, Kevin Le, first describes a password-protected Web application, and then illustrates how the problem arises and discusses the solution to the problem. Although the article is for JSP page elaboration, but the concept elaborated by the author is easy to understand to be able to use for other web technology. Finally, the author shows how to use Jakarta struts to solve this problem gracefully.


Most Web applications do not contain information as confidential as bank accounts or credit card information, but once sensitive data is involved, we need to provide a type of password protection mechanism. For example, a factory worker accesses their schedules through the web, enters their training courses, and looks at their salaries and so on. It's a bit overkill to apply SSL (Secure Socket Layer) at this point, but it's undeniable that we have to provide password protection for these applications, otherwise workers (that is, users of Web applications) can spy on private confidential information from other employees in the factory.
Similar to the above situation, there is a library, hospitals and other public places of the computer. In these places, many users use several computers together, and it is important to protect the user's personal data at this time. Good design and excellent application of the user's expertise requires very little.
Let's take a look at how a perfect Web application in the real world behaves: a user accesses a page through a browser. Web applications show a landing page that requires users to enter valid authentication information. User entered user name and password. At this point we assume that the authentication information provided by the user is correct and that, through the validation process, the Web application allows the user to browse the area he has access to. When the user wants to exit, click the Exit button, the Web application asks the user to confirm whether he really needs to exit, if the user determines to exit, session ends, the Web application is relocated to the landing page. Users can safely leave without worrying that his information will be compromised. When another user sits in front of the same computer, he clicks the Back button and the Web application should not appear on any page that the previous user visited. In fact, a Web application should stay on the landing page until the second user provides the correct authentication information.
Through the sample program, the article explains how to implement this functionality in a Web application.



JSP Example
To more effectively illustrate the implementation scenario, this article starts with a problem that is presented in the logoutSampleJSP1 of an example application. This example represents a number of Web applications that do not correctly resolve the exit process. LogoutSampleJSP1 contains the following JSP pages: login.jsp, home.jsp, secure1.jsp, secure2.jsp, logout.jsp, loginaction.jsp, and Logoutaction.jsp. The pages home.jsp, secure1.jsp, secure2.jsp, and logout.jsp are not allowed to be accessed by unauthenticated users, that is, they contain important information that should not appear in the browser until the user logs in or exits. Login.jsp contains a form for users to enter user names and passwords. The logout.jsp page contains a form that requires the user to confirm whether or not to exit. The loginaction.jsp and logoutaction.jsp are included as controllers for login and exit codes respectively.
The second example application logoutSampleJSP2 shows how to troubleshoot problems in the example logoutSampleJSP1. However, the second application itself is questionable. In certain cases, the exit problem will still occur.
The third example applies logoutSampleJSP3 to the second example, which solves the exit problem more perfectly.
The last example logoutsamplestruts shows how struts resolves the landing problem gracefully.
Note: The example attached here is tested in the latest version of Microsoft Internet Explorer (IE), Netscape Navigator, Mozilla, Firefox, and avant browsers.


Login Action
Brian Pontarelli's classic article "Java EE Security:container versus Custom" discusses different ways of Java EE certification. The article also points out that HTTP protocol and form based authentication do not provide a mechanism to handle user exit. Therefore, the solution is to introduce a custom security implementation mechanism.
A common approach to customizing the security authentication mechanism is to obtain authentication information entered by the user from the form and then authenticate to the security domain such as LDAP (Lightweight Directory Access Protocol) or the relational database. If the user provides authentication information that is valid, the login action injects an object into the HttpSession object. HttpSession there is an injected object that indicates that the user has logged in. To facilitate the reader's understanding, the example attached to this article only writes a username to HttpSession to indicate that the user has logged in. Listing 1 is a section of the code excerpt from the Loginaction.jsp page to illustrate the login action:


Listing 1
//...
Initialize RequestDispatcher object; Set forward to home page by default
RequestDispatcher rd = Request.getrequestdispatcher ("home.jsp");

Prepare Connection and statement
rs = stmt.executequery ("Select password from USER where userName = '" + userName + "");
if (Rs.next ()) {//query only returns 1 a record of the result set; only 1
Password per UserName which is also the primary key
if (rs.getstring ("password"). Equals (password)) {//if valid password
Session.setattribute ("User", userName); Saves username string in the Session object
}
else {//password does not match, i.e., invalid user Password
Request.setattribute ("Error", "Invalid password.");

RD = Request.getrequestdispatcher ("login.jsp");
}
//no record in the result set, i.e., invalid username
else {

Request.setattribute ("Error", "Invalid user name.");
RD = Request.getrequestdispatcher ("login.jsp");
}
}

As a controller, loginaction.jsp finally either forwards to "login.jsp" or "home.jsp"
Rd.forward (request, response);
//...





The examples that are attached to this article use a relational database as a security domain, but the views elaborated in this article apply to any type of security domain.

Logout Action
The exit action contains a simple delete user name and a invalidate () method to invoke the user's HttpSession object. Listing 2 is an excerpt from the loginoutaction.jsp page to illustrate the exit action:


Listing 2
//...
Session.removeattribute ("User");
Session.invalidate ();
//...





Prevent unauthorized access to protected JSP pages
After obtaining the user submitted authentication information from form and verifying, the landing action simply writes a username to the HttpSession object, and the exit action does the opposite work, it deletes the username from the user's HttpSession object and invokes the invalidate () method to destroy HttpSession. In order for the login and exit actions to really work, all protected JSP pages should first verify that the HttpSession contains a username to verify that the current user has logged in. If the httpsession contains a username, which means that the user has logged in, the Web application sends the remaining JSP pages to the browser, otherwise the JSP page jumps to the landing page login.jsp. Page home.jsp, secure1.jsp, secure2.jsp, and logout.jsp all contain the code snippet in Listing 3:


Listing 3
//...
String userName = (string) session.getattribute ("User");
if (null = = UserName) {
Request.setattribute ("Error", "Session has ended.") Please login. ");
RequestDispatcher rd = Request.getrequestdispatcher ("login.jsp");
Rd.forward (request, response);
}
//...
Allow the rest of the the dynamic content in the is served to the browser
//...





In this code snippet, the program httpsession the username string from the inside. If the string is empty, the Web application automatically aborts the current page and jumps to the landing page, giving the session has ended. Please log in; if not NULL, the Web application continues to execute, which is to provide the remaining pages to the user.

Run logoutSampleJSP1
There are several scenarios in which you can run LogoutSampleJSP1:
? If the user does not log in, the Web application will correctly abort the protected page home.jsp, secure1.jsp, Secure2.jsp and logout.jsp implementation, that is, if the user in the browser address bar directly typing the protected JSP page address to try to access, the Web application will automatically jump to the landing page and prompts the session has ended. Please log in.
? Similarly, when a user has exited, the Web application will also properly abort the protected page home.jsp, secure1.jsp, secure2.jsp and logout.jsp execution
? After the user exits, if you click the Back button on the browser, the Web application will not protect the protected page correctly--after the session is destroyed (the user exits) the protected JSP page is displayed again in the browser. However, if the user clicks on any links on the page, the Web app will jump to the landing page and tip the session has ended. Please log in.

Block Browser caching
The root of the problem is that most browsers have a back button. When you click the Back button, the browser does not retrieve the page from the Web server by default, but instead loads the page from the browser cache. Java-based Web applications do not limit this functionality, as is the problem in Web applications based on PHP, ASP, and. Net.
After the user clicks the Back button, the browser to the server and then from the server to the browser, the usual meaning of the HTTP loop is not established, just users, browsers and caching to interact. So, even if the code on listing 3 is included to protect the JSP page, the code will not execute when the back button is clicked.
The good or bad of the cache is really a benevolent. Caching does provide some convenience, but you can usually only feel it using a static HTML page or a graphics or impact page. Web applications, on the other hand, are usually based on data, and the data is often changed frequently. Providing the most up-to-date data is more important than reading from the cache and displaying out-of-date data!
Fortunately, the HTTP header information "Expires" and "Cache-control" provide an application server with a mechanism to control caching on the browser and proxy server. HTTP header information expires tells the proxy server when its cached pages will expire. The newly defined header information in the HTTP1.1 specification Cache-control can inform the browser not to cache any pages. When the Back button is clicked, the browser accesses the server to retrieve the page. The following are basic methods for using Cache-control:
? No-cache: Force cache to get new pages from the server
? No-store: Caching does not save any pages in any environment
The Pragma:no-cache in the HTTP1.0 specification is equivalent to the Cache-control:no-cache in the HTTP1.1 specification and can also be included in the header information.
Using the cache control of HTTP header information, the second example applies logoutSampleJSP2 to solve the problem of logoutSampleJSP1. LogoutSampleJSP2 differs from logoutSampleJSP1 in the following code snippet, which is added to all protected pages:


//...
Response.setheader ("Cache-control", "No-cache"); Forces caches to obtain a new copy of the page from the origin server
Response.setheader ("Cache-control", "No-store"); Directs caches not to store the page under any circumstance
Response.setdateheader ("Expires", 0); Causes the proxy cache to the page as "stale"
Response.setheader ("Pragma", "No-cache"); HTTP 1.0 Backward Compatibility
String userName = (string) session.getattribute ("User");
if (null = = UserName) {
Request.setattribute ("Error", "Session has ended.") Please login. ");
RequestDispatcher rd = Request.getrequestdispatcher ("login.jsp");
Rd.forward (request, response);
}
//...





By setting header information and checking the username in the httpsession, the browser does not cache the page, and if the user does not log on, the protected JSP page will not be sent to the browser, and the landing page login.jsp will be replaced.

Run logoutSampleJSP2
After running logoutSampleJSP2, you will see the following results:
? When the user exits and tries to click the Back button, the browser does not display the protected page, it will only be realistic landing page login.jsp at the same time give the prompt message session has ended. Please log in.
? However, when you press the Back button to return the page is processing the user submitted data page, IE and Avant browser will pop up the following information prompts:
Warning: page has expired ... (You must have met)
Select Refresh the previous JSP page will be displayed again in the browser. Obviously, this is not what we want to see because it violates the purpose of the logout action. When this occurs, it is likely that a malicious user is trying to obtain data from another user. However, this problem only shows up now the back button corresponds to a page that handles post requests.

Record the last landing time
The above problem arises because the browser resubmitted the data in its cache. In the example of this article, the data contains the username and password. Whether or not a security warning message is given, the browser plays a negative role at this time.
In order to solve the problems that arise in logoutSampleJSP2, LogoutSampleJSP3 's login.jsp includes a hidden form field called Lastlogon on the basis of username and password, which is dynamically initialized with a long value. This long value is the number of milliseconds since January 1, 1970 that the call System.currenttimemillis () obtains. When a form in login.jsp is committed, loginaction.jsp first compares the values in the hidden field with the values in the user database. Web applications Consider this a valid login only if the value in the Lastlogon form field is greater than the value in the database.
In order to verify the login, the Lastlogon field in the database must be updated with the Lastlogon value in the form. In the example above, when the browser submits the data repeatedly, the Lastlogon value in the form is not greater than the Lastlogon value in the database, so loginaction go to the login.jsp page and prompt the session has ended. Please log in. Listing 5 is the snippet of the excerpt from Loginaction:


Listing 5
//...
RequestDispatcher rd = Request.getrequestdispatcher ("home.jsp"); Forward to homepage by default
//...
if (rs.getstring ("password"). Equals (password)) {//if valid password
Long lastlogondb = Rs.getlong ("Lastlogon");
if (Lastlogonform > Lastlogondb) {
Session.setattribute ("User", userName); Saves username string in the Session object
Stmt.executeupdate ("Update USER set lastlogon=" + Lastlogonform + "where UserName = '" + userName + "");
}
else {
Request.setattribute ("Error", "Session has ended.") Please login. ");
RD = Request.getrequestdispatcher ("login.jsp"); }
}
else {//password does not match, i.e., invalid user Password
Request.setattribute ("Error", "Invalid password.");
RD = Request.getrequestdispatcher ("login.jsp");
}
//...
Rd.forward (request, response);
//...





In order to achieve the above method, you must record the last login time for each user. For a relational database security domain, this can be easily achieved by adding a lastlogin field to a table. LDAP and other security domains need to be a little bit of a brain, but clearly achievable.
There are many ways to indicate the final landing time. The example logoutSampleJSP3 leverages the number of milliseconds since January 1, 1970. This approach is also possible when many people log in with a user account in different browsers.

Run logoutSampleJSP3
Running the sample logoutSampleJSP3 will show you how to handle the exit problem correctly. Once the user exits, clicking the Back button on the browser will not be protected in any case the page is displayed on the browser. This example shows how to properly handle an exit problem without additional training.
In order to make the code more concise and efficient, some redundant code can be deleted. One way to do this is to write the code in Listing 4 to a separate JSP page that can be referenced by tags <jsp:include> other pages.

The exit implementation under the Struts framework
Another option is to use struts, compared to using JSP or jsp/servlets directly. Adding a framework to handle exit problems for a Web application based on struts can be gracefully achieved without effort. This is partly because struts uses the MVC design pattern so that the model and view are clearly separated. In addition, Java is an object-oriented language that supports inheritance and enables code reuse more easily than scripts in JSP. In struts, the code in Listing 4 can be ported from the JSP page to the Execute () method of the action class.
In addition, we can define a basic class that inherits the struts action class, and the Execute () method contains the code in Listing 4. By using the class inheritance mechanism, other classes can inherit common logic from the base class to set HTTP header information and retrieve the username string in the HttpSession object. This basic class is an abstract class and defines an abstract method ExecuteAction (). All subclasses that inherit from the base class should implement the Exectuteaction () method rather than overwrite it. Listing 6 is part of the code for the base class:


Listing 6
Public abstract class Baseaction extends Action {
Public Actionforward Execute (actionmapping mapping, Actionform form,
HttpServletRequest request, HttpServletResponse response)
Throws IOException, Servletexception {

Response.setheader ("Cache-control", "No-cache"); Forces caches to obtain a new copy of the page from the origin server
Response.setheader ("Cache-control", "No-store"); Directs caches not to store the page under any circumstance
Response.setdateheader ("Expires", 0); Causes the proxy cache to the page as "stale"
Response.setheader ("Pragma", "No-cache"); HTTP 1.0 Backward Compatibility

if (!this.userisloggedin (request)) {
Actionerrors errors = new Actionerrors ();

Errors.add ("Error", New Actionerror ("logon.sessionended"));
This.saveerrors (request, errors);

Return Mapping.findforward ("sessionended");
}

return executeAction (mapping, form, request, response);
}

Protected abstract Actionforward executeAction (actionmapping mapping,
Actionform form, httpservletrequest request, httpservletresponse response)
Throws IOException, Servletexception;

Private Boolean Userisloggedin (HttpServletRequest request) {
if (Request.getsession (). getattribute ("User") = = null) {
return false;
}

return true;
}
}




The code in Listing 6 is very much like that in Listing 4, just replacing RequestDispatcher forward with actionmapping Findforward. In Listing 6, if the username string is not found in HttpSession, the Actionmapping object will find the forward element named Sessionended and jump to the corresponding path. If found, the subclass executes its business logic that implements the ExecuteAction () method. Therefore, it is necessary to declare a sessionended forward element for all subclasses in the configuration file Struts-web.xml. Listing 7 illustrates such a statement with the Secure1 action:

Listing 7
<action path= "/secure1"
Type= "Com.kevinhle.logoutSampleStruts.Secure1Action"
Scope= "Request" >
<forward name= "Success" path= "/web-inf/jsps/secure1.jsp"/>
<forward name= "sessionended" path= "/login.jsp"/>
</action>




The subclass Secure1action inherits from the Baseaction class implements the ExecuteAction () method rather than overwriting it. The Secure1action class does not execute any exit code, as shown in Listing 8:


public class Secure1action extends Baseaction {
Public Actionforward executeAction (actionmapping mapping, Actionform form,
HttpServletRequest request, HttpServletResponse response)
Throws IOException, Servletexception {

HttpSession session = Request.getsession ();
Return (Mapping.findforward ("Success"));
}
}





This solution is elegant and effective only if you need to define a base class without the need for additional code work. In any case, it is worth recommending that the generic behavioral approach be written as a base class of inherited strutsaction, a common experience for many struts projects.

Limitations
The above solution is very simple and practical for JSP or struts based Web applications, but it has some limitations. In my view, these limitations are not essential. (Limitations not translated, see original)

Conclusion
This article describes the solution to the exit problem, although the solution is surprisingly simple, but in all cases can work effectively. Whether it's a JSP or struts, all you have to do is write a code that does not exceed 50 lines and a way to record the user's last login time. Mixing these scenarios in a Web application can keep private data that is not compromised, while also increasing the user's experience.



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.