Pre-used JS form to prevent duplicate submission method
The code is as follows |
Copy Code |
<script type= "Text/javascript" > var checksubmitflg = false; function Checksubmit () { if (!CHECKSUBMITFLG) { First time Submission CHECKSUBMITFLG = true; return true; } else { Repeat Submission Alert ("Submit again!"); return false; } } </script> The following three methods are called separately <form onsubmit= "return Checksubmit ();" > <input type= "Submit" onclick= "return Checksubmit ();"/> <input type= "button" onclick= "document.forms[0].action= './test ';d ocument.forms[0].submit (); return CheckSubmit ( ); "/> |
so if I make a form directly, and then submit it to/test, the above agent is a device, then how do we solve this problem
If you already know how to solve this article may not be suitable for your taste, Paperen here also intends to start from the basis of discussion, so I hope that one step to see the solution you may not be appropriate, so please note. so~ start.
Paperen Think you must know what the form is, the form element is the form, the General page needs to enter the place must use the form element, also very common, the general code is as follows:
code is as follows |
copy code |
<form method= "POST" <p> <label for= "Test" > casually enter something www.111cn.net</label> <input type= "text" name= "data" id= "test"/> </p> <p> <input type= "Submit" value= "submitted" name= "Submission"/> </p> & nbsp; </ul> </form> |
The focus is actually form and input elements, p element is only Paperen privately added, to follow the instructions without any impact, in fact, is very simple, the so-called input is entered, you can fully interpret the input elements as a user input, Only some of the attributes (type) cannot be input (this is the submit), and the form element you can completely interpret as a bag that takes all the user input data to the server after it is loaded into it, but the method attribute is notable for the form element. In general there are get and post two methods, do not think too complex (because the depth does not need to understand, for the subsequent content is not too much relationship, if interested may wish to use the browser's debugging tools to view the request header information and send information, such as Firebug), show that is, The value of all input elements will appear in the Address bar when you submit the form using get, and post will not, such as the browser address bar when you submit this form using get
The code is as follows |
Copy Code |
Http://localhost/mytest/token/form.php?data=test&submit=%E6%8F%90%E4%BA%A4 |
Post is not visible in the address bar, you can use Fiebug to see the following information
It is easy to assume that get is an explicit transfer of data, while post is an implicit transfer of data, but there is a big difference is that the post supports more and larger data transfer.
Next, when the form code is written, let's write the server script (PHP here). Very simple ~
code is as follows |
copy code |
<?php if (isset ($_post[' submit ')) { /form Submit processing $data = isset ($_post[' data '])? Htmlspecialchars ($_post[' data '): '; // Insert or UPDATE database $sql = INSERT INTO Test (' string ') VALUES (' $data '); / /do query echo $sql; } |
Because this is the post transfer data, so using PHP's $_post global variable to get the form submission of data, all the use of the POST method of the form data submitted to the server will be saved in this $_post global variable, may wish to try Print_r ($_post) You will understand this variable.
First check whether there is a submit in the $_post array, if the existence of the proof is the form submitted, as if there is a asp.net called IsPostBack, but this is not so rigorous, but it does not matter after the problem will be solved.
Then you receive the input box data, which is $_post[' data ', and don't forget to use Htmlspecialchars to do HTML filtering on this, because it prevents you from entering HTML tags or JavaScript as a problem (seemingly an XSS vulnerability). Finally, the stitching into the SQL statement into the database ran (just here Paperen did not use a lot of detail to manipulate the database functions such as mysql_query, interested in the completion of its own). Congratulations, here you have successfully completed a data entry function, but there is a place you have to improve it, insert the data must give the operator a hint of it ~ ~ at least prompted me to fail or successful operation. So the entire code paperen written like this.
The code is as follows |
Copy Code |
<?php
if (Isset ($_post[' submit ')) {
Form submission Processing
$data = Isset (
$_post[' data '])? Htmlspecialchars ($_post[' data '): ';
Connect
mysql_connect (' localhost ', ' root ', ' root ');
Select DB
mysql_select_db (' Test ');
Set character set to prevent garbled
mysql_query (' Set names "UTF8");
Sql
$sql = "Insert
Into ' token ' (string) VALUES (' $data ');
Query
mysql_query ($sql);
$insert _id = mysql_insert_id ();
if (
$insert _id) {
$state = 1;
} else {
$state = 0;
}
}
?> <?php if (isset ($state) && $state) {//Data insertion Success?> <p> Insert Success <a href= "form.php" > Return </a></p> <?php} else {//failed or no insert move As?> <form method= "POST" > <p> <label for= "Test" > Random input points </label> <input type= "Text" Name= "Data" id= "test"/> </p> <p> <input type= "Submit" value= "submitted" name= "Submission"/> </p> </ul> </form> <?php}?> |
HTML declaration and head and body are omitted, compared to the beginning of the code is actually the main implementation of the actual insert database action and given the operation feedback (through the $state variable), may wish to copy the code and then try (of course, according to their actual situation to modify the database operation part of the code). Code normal, logical no problem, but there is a problem, that is, after the success of the display insert and then refresh the page will perform the form processing action, and inserted again data! This is called a repeat insertion problem. You can think about how to solve the problem before releasing the solution.
Do you think that receiving data and display processing results are the same page so that this problem can be caused? Yes, you can also think that, with some debugging tools you'll find that the browser also retains the Post's data, so the post data is resubmitted once the form has been submitted and then refreshed.
If there is a way to empty the browser this temporary saved post data will not solve the problem, but the server is not able to do this, because this is the browser itself, or we are redirected or otherwise refresh or will repeat the submission of data.
So far you may have learned the meaning and the problem of repeated submissions, if you do not choose the redirection method then you have to think of another way, so the token solution is this way.
Just as the token itself represents permissions, the right to operate, identity, etc., so I can add such an identity flag for my form, when the client requests the form at the same time to generate a token of its hook, at the time of submission to judge, correct to receive and process the form. The realization of the essence is so, and reflected to the concrete implementation, you need to use a call session of things. For a session resolution, see the Wiki
The simple understanding is that the session is also a token concept, so you might be surprised, "What am I already using a token?" "Yes, but we're going to do more than just the session but attach some data to the form token we want to implement." So let ' s do it!
Session in PHP is also stored in the $_session of this super global variable, to enable the use of session_start (), for other server-side scripting principles, just may invoke method name inconsistencies. The code after adding token is as follows:
The code is as follows |
Copy Code |
<?php
Open session
Session_Start ();
if (Isset ($_post[' submit ')) && Valid_token ()) {
Form
Submit Processing
}
/**
* Generate token
* @return String MD5 The time stamp after encryption
*/
function Create_token () {
Current time stamp
$timestamp = time ();
$_session[' token ' = $timestamp;
return MD5 ($TIMESTAMP);
}
/**
* Whether valid token
* @return BOOL
*/
function Valid_token () {
if (Isset ($_session[' token ')) && isset ($_post[' token ']) && $_post[' token '] = = MD5 (
$_session[' token '])
{
If the token is properly destroyed
$_session[' token '] = ';
return true;
}
return false;
}
?>
<?php if (isset ($state) && $state) {//Data insertion Success?>
<p> Insert Success <a href= "form.php" > Return
</a></p>
<?php} else {//failed or no Insert action?>
<form method= "POST" >
<p>
<label for= "Test" > Casual
Enter a point what </label>
<input type= "text" name= "data" id= "test"/>
</p>
<p>
<input type= "Submit" value= "mention
Pay "name=" Submit "/>"
</p>
</ul>
<!--Token-->
<input type= "hidden" value= "<?php Echo Create_token (); Generated
Token?> "name=" token "/>
<!--Token-->
</form>
<?php}?> |
Part of the code Paperen here omitted, because it is not the point, actually add only 3 things:
First, add an INPUT element before the form ends, remember type is hidden (hidden field)
Second, add two functions, Create_token and Valid_token, which are used to generate tokens that are used to validate tokens
Third, add a condition to the IF, Valid_token
That's it, it's simple, and everything is clustered in two new functions. Paperen Here the token is very simple is the timestamp, the request form when the timestamp stored in the $_session[' token ', then the verification token is understood, is to check the client submitted $_post[' token ' after MD5 session[' token '] is consistent and, of course, there are two variables of $_post[' token ' and $_session[' token '.
You can encapsulate this simple token pattern in a much better way and extend the functionality, such as adding a form submission Timeout verification is a good hands-on opportunity.
Finally, attach the Form_validation class file of the previous Paperen extension codeingeter, mainly the extended token and the form timeout. The welcome.php in the compressed package is the controller file, please place it in the Applicationcontroller (if you do not want to increase the controller to open and then copy the token method to the existing other controller); my_form_ validation.php please put it in the applicationlibraries.
Codeingeter form_validation class file code
The code is as follows |
Copy Code |
<?php if (! defined (' BasePath ')) exit (' No Direct script access allowed '); Class Welcome extends Ci_controller { Public Function Index () { $this->load->view (' welcome_message '); } Public Function token ()
{
$this->load->helper (' form ');
$this->load->library (' form_validation ');
if ($this->input->post (' Submit ') && $this->form_validation->valid_token ())
{
Nothing
Valid_token already contains token timeout and token correct judgment, to enable token timeout, set Token_validtime to non 0
echo ' OK ';
} Generate Form Token $token = $this->form_validation->create_token (); form Example Echo Form_Open (); echo form_input (' token ', '); Echo $token; echo form_submit (' Submit ', ' submit '); Echo Form_close (); } } |
Form_validation_token
The code is as follows |
Copy Code |
<?php if (! defined (' BasePath ')) exit (' No Direct script access allowed '); /** * @abstract the Form_validation class that inherits CI increases the token on its basis */ Class My_form_validation extends Ci_form_validation { /** * Token key value * @var String */ var $key = ' token ';
/** * Effective time of Form token (SEC) * @abstract If some forms need to limit input time, set this value to 0 without limiting * @var int seconds */ var $token _validtime = 5; /** * Debug Mode * @var BOOL */ var $debug = false; /** * CI objects * @var <type> */ Private $_ci; Public Function __construct () { Parent::__construct (); $this->_ci =& get_instance (); If the configuration is not filled in Encryption_key $encryption _key = Config_item (' Encryption_key '); if (Empty ($encryption _key)) $this->_ci->config->set_item (' Encryption_key ', $this->key); If the session is not loaded if (!isset ($this->_ci->session)) $this->_ci->load->library (' Session '); } /** * Set token valid time * @param int $second seconds */ Public Function Set_token_validtime ($second) { $this->token_validtime = intval ($second); } /** * Get form token valid time * @return int number of seconds */ Public Function Get_token_validtime () { return $this->token_validtime; } /** * Verifying that the form token is valid * @return bool & nbsp; */ public Function Valid_token () { if ($this->debug) return true; //Get hash in session $ Source_hash = $this->_ci->session->userdata ($this->key); if (empty ($source _hash)) return false; //Determine if timeout if ($this->is_token_exprie ()) return false; The hash submitted $post _formhash = $this->_ci->input->post ($this->key); if (Empty ($post _formhash)) return false; if (MD5 ($source _hash) = = $post _formhash) { $this->_ci->session->unset_userdata ($this->key); return true; } return false; } /**
* Generate form token (along with INPUT element)
* @return String
*/
Public Function Create_token ($output = False)
{
$code = Time (). '|' . $this->get_random (5);
$this->_ci->session->set_userdata ($this->key, $code);
$result = function_exists (' Form_hidden ')? Form_hidden ($this->key, MD5 ($code)): ' <input type= ' hidden ' name= ' token ' value= '. MD5 ($code). '/> ';
if ($output)
{
echo $result;
}
Else
{
return $result;
}
}
/**
* Get random number (you can extend it yourself)
* @param int $number Upper Limit
* @return String
*/
Public Function Get_random ($number)
{
Return rand (0, $number);
}
/**
* Determine if the form token is expired
* @return BOOL
*/
Public Function Is_token_exprie ()
{
if (Empty ($this->token_validtime)) return false;
$token = $this->_ci->session->userdata ($this->key);
if (empty ($token)) return false;
$create _time = array_shift (Explode (' | ', $token));
Return (Time ()-$create _time > $this->token_validtime);
}
} |