CSC/ECE 517 Fall 2009/wiki2 3 b5: Difference between revisions
Chump Chief (talk | contribs) |
Chump Chief (talk | contribs) |
||
Line 10: | Line 10: | ||
== Example application == | == Example application == | ||
Java from [http://www.javaworld.com/javaworld/javatips/jw-javatip136.html?page=2 JavaWorld] | |||
<blockquote><pre> | |||
public final ActionForward perform(ActionMapping mapping, | |||
ActionForm form, | |||
HttpServletRequest request, | |||
HttpServletResponse response) | |||
throws IOException, ServletException { | |||
HttpSession session = request.getSession(); | |||
ActionForward forward = null; | |||
if (isTokenValid(request)) { | |||
// Reset token and session attributes | |||
reset(request); | |||
try { | |||
// Perform the action and store the results | |||
forward = performSynchro(mapping, form, request, response); | |||
session.setAttribute(FORM_KEY, form); | |||
session.setAttribute(FORWARD_KEY, forward); | |||
ActionErrors errors = (ActionErrors) | |||
request.getAttribute(Action.ERROR_KEY); | |||
if (errors != null && !errors.empty()) { | |||
saveToken(request); | |||
} | |||
session.setAttribute(ERRORS_KEY, errors); | |||
session.setAttribute(COMPLETE_KEY, "true"); | |||
} catch (IOException e) { | |||
// Store and rethrow the exception | |||
session.setAttribute(EXCEPTION_KEY, e); | |||
session.setAttribute(COMPLETE_KEY, "true"); | |||
throw e; | |||
} catch (ServletException e) { | |||
// Store and rethrow the exception | |||
session.setAttribute(EXCEPTION_KEY, e); | |||
session.setAttribute(COMPLETE_KEY, "true"); | |||
throw e; | |||
} | |||
} else { | |||
// If the action is complete | |||
if ("true".equals(session.getAttribute(COMPLETE_KEY))) { | |||
// Obtain the exception from the session | |||
Exception e = (Exception) session.getAttribute(EXCEPTION_KEY); | |||
// If it is not null, throw it | |||
if (e != null) { | |||
if (e instanceof IOException) { | |||
throw (IOException) e; | |||
} else if (e instanceof ServletException) { | |||
throw (ServletException) e; | |||
} | |||
} | |||
// Obtain the form from the session | |||
ActionForm f = (ActionForm) session.getAttribute(FORM_KEY); | |||
// Set it in the appropriate context | |||
if ("request".equals(mapping.getScope())) { | |||
request.setAttribute(mapping.getAttribute(), f); | |||
} else { | |||
session.setAttribute(mapping.getAttribute(), f); | |||
} | |||
// Obtain and save the errors from the session | |||
saveErrors(request, (ActionErrors) | |||
session.getAttribute(ERRORS_KEY)); | |||
// Obtain the forward from the session | |||
forward = (ActionForward) session.getAttribute(FORWARD_KEY); | |||
} else { | |||
// Perform the appropriate action in case of token error | |||
forward = performInvalidToken(mapping, form, request, response); | |||
} | |||
} | |||
return forward; | |||
} | |||
</pre></blockquote> | |||
== Critique == | == Critique == |
Revision as of 01:08, 10 October 2009
Synchronizer Token Pattern
Problem summary
Many websites rely on synchronous activity between the client and server. This synchronization can be disrupted if the client takes actions in an order not expected by the server. For instance, when submitting a purchase in an online store, the client might click the "Purchase" button multiple times. They might hit their browser's back button and take another action while the transaction is still processing. These types of actions could produce unpredictable results and must be protected against.
Overview
The Synchronizer Token pattern is a server side solution to this problem. The concept is fairly simple: Establish a token on the server side that indicates a valid submission, and give a token signature to the client that corresponds to that token (most likely in a hidden input field). When the client submits their form, the server validates their token and proceeds. It then marks the token as invalid so it may not be used again. The result is that any given form may only be used once and then will not work again.
Example application
Java from JavaWorld
public final ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { HttpSession session = request.getSession(); ActionForward forward = null; if (isTokenValid(request)) { // Reset token and session attributes reset(request); try { // Perform the action and store the results forward = performSynchro(mapping, form, request, response); session.setAttribute(FORM_KEY, form); session.setAttribute(FORWARD_KEY, forward); ActionErrors errors = (ActionErrors) request.getAttribute(Action.ERROR_KEY); if (errors != null && !errors.empty()) { saveToken(request); } session.setAttribute(ERRORS_KEY, errors); session.setAttribute(COMPLETE_KEY, "true"); } catch (IOException e) { // Store and rethrow the exception session.setAttribute(EXCEPTION_KEY, e); session.setAttribute(COMPLETE_KEY, "true"); throw e; } catch (ServletException e) { // Store and rethrow the exception session.setAttribute(EXCEPTION_KEY, e); session.setAttribute(COMPLETE_KEY, "true"); throw e; } } else { // If the action is complete if ("true".equals(session.getAttribute(COMPLETE_KEY))) { // Obtain the exception from the session Exception e = (Exception) session.getAttribute(EXCEPTION_KEY); // If it is not null, throw it if (e != null) { if (e instanceof IOException) { throw (IOException) e; } else if (e instanceof ServletException) { throw (ServletException) e; } } // Obtain the form from the session ActionForm f = (ActionForm) session.getAttribute(FORM_KEY); // Set it in the appropriate context if ("request".equals(mapping.getScope())) { request.setAttribute(mapping.getAttribute(), f); } else { session.setAttribute(mapping.getAttribute(), f); } // Obtain and save the errors from the session saveErrors(request, (ActionErrors) session.getAttribute(ERRORS_KEY)); // Obtain the forward from the session forward = (ActionForward) session.getAttribute(FORWARD_KEY); } else { // Perform the appropriate action in case of token error forward = performInvalidToken(mapping, form, request, response); } } return forward; }