I am exploring JSP to implement dynamic web pages. One issue to solve is navigation between pages. Users have the possibility to go back and forward in their browsers.
This has to be handled. If someone logs out (for example), we don't want someone else to retrieve the session or data by clicking 'go back'. Another example is that we don't want to resubmit forms twice.
I am looking for tips and advices to solve page navigation issues. I would like to create a list of issues one has to take care of + possible solutions:
Issues:
Making sure sessions cannot be retrieved/hijacked with go back/forward clicks
Making sure forms and not submitted twice
Making sure users cannot fiddle cookies or URL data or hidden fields to break control flow and security
Solutions:
Implement a stack of visited pages
When a page is invoked, register the moment it is displayed to differentiate new requests from 'go back'
Control current session
P.S.: I have seen this question, but there is no real answer to it.
Making sure sessions cannot be retrieved/hijacked with go back/forward clicks
Just disable browser cache of those pages. See also Prevent user from seeing previously visited secured page after logout.
Making sure forms and not submitted twice
Generate a long, unique and impossible-to-guess string, store it in both the session ..
String token = UUID.randomUUID().toString();
((Set<String>) session.getAttribute("tokens")).add(token);
request.setAttribute("token", token);
request.getRequestDispatcher("/WEB-INF/page.jsp").forward(request, response);
.. and as a hidden input field of the form.
<input type="hidden" name="token" value="${token}" />
Upon submit, compare and remove the key in the session. If it was in the session, then proceed with submit.
if (((Set<String>) session.getAttribute("tokens")).remove(request.getParameter("token")) {
// Valid token. Proceed with submit.
} else {
// Invalid token. Possible double submit.
}
Making sure users cannot fiddle cookies or URL data or hidden fields to break control flow and security
Just write robust code yourself or use an existing and robust MVC framework like JSF2, Spring-MVC, Struts2, etc.
Solutions:
Implement a stack of visited pages
When a page is invoked, register the moment it is displayed to differentiate new requests from 'go back'
Control current session
Cumbersome.
To prevent double submission, I will use an example Apache Struts 1 used by using HttpSession.
What Struts did was to generate a random token that is stored in a session and added in a form page in a presentation layer (JSP). When a user submit a form, it checks from the session to see if the token given by the form is exactly the same session found in the session. If it's true, then process the request else it's a double submission.
Example:
public class AuthenticationAction extends Action {
public void displayLogout(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
saveToken(request);
return mapping.findForward("displayLogout");
}
public ActionForward doLogout(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
if (isValidToken(request)) {
//It wasn't yet a double submission, delete token generated by Struts
resetToken(request);
//logout.
...
//return back home
return mapping.findForward("home");
} else {
//Double submission..
throw new Exception("double submission");
}
}
}
A better tutorial is found here.
Hmm, you can also use the Spring Webflow framework if you want, but the use of the back and refresh not submitting your forms twice can easely be solved by defining your controller right. I think the use of REST can also help solve some problems.
The hiddenfield manipulation is another thing since a hiddenfield can always be viewed in the source of your page. And if the field can be viewed then it is open to manipulation.
To avoid re-inventing the wheel, using an existing framework seems to be the best solution. Struts looks like a good candidate. A simple introduction tutorial is available.
Related
I have a spring web app using thymeleaf on the front end. I have a controller on my backend that adds an item to the database. This same functionality is present across pages so it can be called from multiple urls. When the method is done I would like it redirect to the original url view.
So for example in this method:
#GetMapping(value={"/addBook/{id}")
public String addBookToCart(#PathVariable (value="id") int bookId) {
// database functionality
return "redirect:/"
}
It would always return the redirection to /. But I want to be able to return to the page from where the user pressed the button. Essentially I want the saving to database functionality to happen in the background, without the user necessarily noticing. Is there a way to accomplish that? I was wondering if maybe I should be using POST instead of GET, but this isn't tied to a form, just a url upon clicking a button. Also you still typically return some view in a PostMapping I thought, so I am not sure how that'll solve my problem.
I have a very simple JSP page where it has one search box and based off the input, in the search box, it will return a response with a submit button to get the following response.
I noticed that whenever I use request.getattribute("foo") in my servlet to retrieve some request it returns null due to the request ending so I looked at the answers on here and started using session.getattribute("foo") instead. However, now I am stuck having session variables responses being set and it is causing my view to have old session data that isn't suppose to be there so now I have to use session.removeAttribute("foo"), whenever, I don't want that particular response data to be shown.
Is there a better way to go about managing this instead of having to use session.getattribute("foo"), session.removeAttribute("foo") and session.setattribute("foo")?
You should work with request.getSession()
Returns the current session associated with this request, or if the request does not have a session, creates one.
Set an attribute:
request.getSession().setAttribute("foo")
And get attribute using:
request.getSession().getAttribute("foo")
It will be used in the context of the request and not effect other requests, so you don't need to remove attribute.
Read more in Servlets tutorial
Servlets provide an outstanding technical solution: the HttpSession API. This is a high-level interface that allows the server to "remember" a set of information relevant to a particular user's on-going transaction, so that it can retrieve this information to handle any future requests from the same user.
You can go for request.getparameter("foo") or request.setparameter("foo", obj)
This can be used for every request, and it will not add to your session variables and basically will not make your "session object heavy".
Java doc:
Request parameters are extra information sent with the request. For HTTP servlets, parameters are contained in the query string or posted form data.
EDIT: changed file name to 'follow-up.jsp' for clarity.
This is a shorter, and hopefully better version of a longer question I posted earlier today (link: Java web development: transfer control from one servlet to another while passing the request object).
I have a form that is POST-ed upon submit. In the corresponding servlet to which the POST action is done, I want to show either the form page again or a follow-up page, depending on whether validation succeeds. BOTH the form page AND the follow-up page need the form data submitted by the user; the form page needs it to re-populate the form fields, and the follow-up page needs it to show some of this data (for example, to have the user double-check that it is correct). Therefore, I want the request object containing the submitted data to be available to BOTH pages.
Forwarding back to the form is not a problem:
RequestDispatcher rd =
getServletContext().getRequestDispatcher("/form.jsp");
rd.forward(request, response);
But how do I reach the follow-up page (say, follow-up.jsp) in such a way that the request object containing the submitted form data is still accessible in that follow-up page?
The following doesn’t seem to work:
response.sendRedirect("/MyProject/follow-up.jsp");
This is because if “name” is a form parameter that was assigned to an attribute of the request object in the form handling servlet, with this attribute conveniently also being called "name", then the following line in follow-up.jsp:
Your name is: ${name}.
does NOT print the user-submitted name (instead it prints nothing, i.e. only “Your name is:”). In other words, the attributes of the original request object are no longer available in follow-up.jsp. This makes sense in that response.sendRedirect triggers a new HTTP-request (with its own request and response objects). But is not what I want.
Another (secondary) concern is that sendRedirect is unable to issue POST-requests (it only uses GET; see [link] response.sendRedirect() from Servlet to JSP does not seem to work).
EDIT: Also, I realise I could actually use:
rd.forward(request, response);
for BOTH requests. However, that would result in both pages having the same URL showing in the address bar (i.e. the name of the routing servlet, like "...../Register"). And that is definitely bad, because the form page and the follow-up are completely different pages; the follow-up page has little to do with the form (except for displaying some of the submitted form data).
Thanks.
In this case I believe you need to store the data not in request, but in a conversation scope....or in the session scope...regarding redirection not supporting post...then ideally all redirections by definition should be get requests only.....if you want to redirect using a a POST request please re-evaluate your approach...
what is the best practice or best way of passing form parameters from page to page in a flow? If I have a flow where a user enters data in a form and hits next and repeats this process until they get to an approval page, what ways could I approach this problem to make the retention of data as simple as possible over the flow?
I guess you could put all the information as you go in the session but could you get into memory issues if a lot of people are using your app and going through the flow at the same time?
You can store data into Cookies or store them into Session and access them between different web pages.
HttpSession is your best bet if you want to track a "wizard" style data entry.
Just seconding #Rachel's openion.
The server side component that handles your page submits ( such as Servlets) would have some code like:
public void doPost (HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
HttpSession session = req.getSession();
session.setAttribute("Variable1", request.getParameter("input1"));
//and so on..
HttpSession Provides a way to identify a user across more than one page request or visit to a Web site and to store information about that user.
You may also put attributes in the request scope. They are accessed the same way from EL in your JSPs, but do not require a session. Depending on your situation, you may not wish to start sessions for every user.
I'm looking for a method or current API that allows you to add on tokens to web app requests.
Maybe within the session but not persisted.
Or if you could help me by outlining an efficient method for doing this
E.g.
1. GET request => Servlet generates a token and prints it in the view
2. returns a view with a hidden token
<input type="hidden" name="token" value="UA37jdjs9UDJS3">
<input type="submit" name="deleteEmail" value="Delete">
3. POST request => form is submitted and checks if the token is the same.
Few things to note, If there are Ajax requests then some other tokens would have to be alive for a number of requests.
If the user decides to close the browser, the token would have to die when the session
is timed-out.
If the user fails to complete the form, goes off to do something else on the site,
those tokens would have to be deleted as they go unused.
But what is the best way of implementing a system like this,
Does Spring Security 3 have a system that i can use?
within the Java,Grails,Spring MVC, Spring Security 3 and Hibernate area
Have a look at the HDIV project at http://www.hdiv.org/. They do exactly this. Even if you don't use the HDIV project's code then the information there may give you an option how to do it yourself. It was a good primer for me to learn about handling tokens for things like CSRF and other uses like double submit controls.
Did you take a look at "Synchronizer Token Pattern" in the Grails documentation at http://grails.org/doc/1.2.0/guide/single.html ?
First thought was that you might just use the already generated session id. But if you are trying to fork state I would suggest to use something like seams conversation model
Why don't just uses the session_id that the Web container generates for you when you call request.getSession()?
If you want to create your own "token" you might want to check Cookies.
A Cookie is a key-value pair sent as an HTTP header by a web server to a web browser and then sent back unchanged by the browser each time it accesses that server.
To create a cookie in a Servlet you can uses:
public void doGet ( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException {
// Create a cookie
Cookie c1 = new Cookie("yourdomain.token","the value");
response.addCookie(c1);
//build your response
}
The cookie will be automatically included in the next http request. You can read it back with:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
//build your response
}
I recently encountered a use case for this.
If an old application window was present in the browser, and a login link was clicked from another browser window, the login action first created a new session, and then redirected to the application window. This triggered the old window's onunload method method, which resulted in a logout request to the server logging out the new user.
Relying on a javascript onunload event for logging out seems kind of crappy to me, but this could not be changed, so we chose to do as the OP suggested and added a token in each rendered view, checking it for each request. This stops the onunload logout request from terminating the new session.
As to the best way, I would say this is pretty straightforward. You can for instance use http://java.sun.com/j2se/1.5.0/docs/api/java/util/UUID.html to generate unique keys. If you are using a component-based framework like Tapestry, JSF or Wicket there might be a more high-level way of handling this.
Is this similar to your usecase? Or are you trying to achieve something completely different?