See my GitHub project Dynamic-Server for the complete code

There will always be a response regardless of the status code 404 200, regardless of whether the request path exists on the server or not, but the status code 200 means that the request was successful and Ajax can execute it. It’s just a 404 status code that says you failed the request, and Ajax will execute the second function in.then.

Concept: dynamic server and static server

Criteria – Whether the database is requested

  • This server does not request database, it is static server (static web page)
  • This server requests the database, which is the dynamic server (dynamic web page)

About Database

  • Databases don’t belong on the front end, but programmers should know a bit about databases

Basic operation

This blog uses json files directly as a database

Create db/users.json in the same directory of the server. The basic structure of the file content is as follows

[{"id": 1."name": "jack"."password": "qqq" },
  { "id": 2."name": "Rose"."password": "kkk"},]Copy the code

Note: Empty the database into an empty array, do not empty all

How to read database to server

  • Read the contents of the users.json file in the database and turn it into a string named usersString
  • Convert the JSON string userString into a JS object (array)

How do I write a database to the server

  • Create a new user information in the server
  • Push the new user information into the usersArray array just generated in the server
  • Convert the usersArray array back into a JSON string
  • Write it back to the users.json file in the database
//fs is used to read files
const fs = require("fs");

// How to read database to server
const usersString = fs.readFileSync("./db/users.json").toString();// Read the contents of the users.json file in the database and make it a string named usersString
const usersArray = JSON.parse(usersString);// Convert the JSON string userString into a JS object (array)

// How to write database in server
const user3 = { id: 3.name: "tom".password: "yyy" };// Create a new user in the server
usersArray.push(user3);// Push the new user information to the usersArray array just generated in the server
const string = JSON.stringify(usersArray);// Convert the usersArray array back to a JSON string
fs.writeFileSync("./db/users.json", string);// write it back to the users.json file in the database
Copy the code

Goal 1: User registration

The effect

  1. The user submits the user name and password
  2. There is a new line of data in the users.json file
  3. When the user successfully registers, the login page is displayed

Train of thought

  1. Write a form in front and let the user fill in name and password
  2. The front end listens for the Submit event
  3. The front end sends a POST request with the data in the request body
  4. The back end receives the POST request
  5. The back end gets the name and password in the request body
  6. The back end writes data to the database

Specific steps

1. Write a form in front and let the user fill in name and password

  1. New register.html, this is the register page (go straight in, don’t set route, because static server)
  2. Write a form with an input of name and password and register a button

2. The front end listens to the Submit event

  1. Reference to the jQuery
  2. Write JS directly in HTML with script tags
  3. Listen for submission events for form labels

3. The front end sends a POST request with data in the request body

When the form is submitted, the function needs to be executed

  • Prevents the default event of the form form

  • Get user data: find the element whose input name is name, take the user input value of this element, named name; Similarly, find the element whose input name is password, take the value entered by the user of this element, and name it password

  • POST request with jquery. ajax: To upload user data to the server, the server stores the data to the database

    (1) a POST request

    ② The requested URL is /register

    ③ The data sent to the server is the JSON string with the user data just obtained in the request body

    ④ The request body type, that is, the data type sent to the server, is JSON

    /sign_in.html); /sign_in.html; / /sign_in.html; / /sign_in.html; / /sign_in.html;


      
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <! -- Mobile terminal please move to Taobao copy is done -->
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover"
    />
    <title>registered</title>
  </head>
  <body>
    <form id="registerForm">
      <div>
        <label>The user name<input type="text" name="name"/></label>
      </div>
      <div>
        <label>password<input type="password" name="password"/></label>
      </div>
      <div><button type="submit">registered</button></div>
    </form>

    <! If you want to use jQuery, you can use javascript.
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>

    <! Write js directly in HTML.
    <! -- Listen for the click event of the form element, and execute the function ① to prevent the default event of the form when the click event is triggered. ② Find the element whose input name is name, take the user input value of this element and name it as name; 3) select 'password' from 'user' and name it 'password'. POST request, the url of the request is /register, the body of the request to the server is a JSON string of the user data just obtained, and the body of the request type is the data type sent to the server. Then set the function after the request succeeds or fails -->
    <! -- Prevent default events -->
    <script>
      const $form = $("#registerForm");
      $form.on("submit", e => {
        e.preventDefault();
        const name = $form.find("input[name=name]").val();
        const password = $form.find("input[name=password]").val();
        console.log(name, password);
        $.ajax({
          method: "POST".url: "/register".contentType: "text/json; charset=UTF-8".data: JSON.stringify({ name, password })
        }).then(
          (a)= > {
            alert("Registration successful");
            location.href = "/sign_in.html"; }, () => {}); });</script>
  </body>
</html>

Copy the code

4. The backend receives the POST request

Write the corresponding route /register, and also a POST request! Is important! Distinguish the request file register.html

5. The backend obtains the name and password in the request body

6. Back-end storage data

Read the database first, then write the new data to the database

var http = require("http");
var fs = require("fs");
var url = require("url");
var port = process.argv[2];

if(! port) {console.log("Please specify a port number, ok? \nnode server.js 8888);
  process.exit(1);
}

var server = http.createServer(function(request, response) {
  var parsedUrl = url.parse(request.url, true);
  var pathWithQuery = request.url;
  var queryString = "";
  if (pathWithQuery.indexOf("?") > =0) {
    queryString = pathWithQuery.substring(pathWithQuery.indexOf("?"));
  }
  var path = parsedUrl.pathname;
  var query = parsedUrl.query;
  var method = request.method;

  /******** start here, don't look at ************/ above
  console.log("There's a request from some idiot! The path (with query parameters) is:" + pathWithQuery);

  if (path === "/register" && method === "POST") {
    response.setHeader("Content-Type"."text/html; charset=utf-8");
    // Read the database
    const userArray = JSON.parse(fs.readFileSync("./db/users.json"));
    // Select name and password from the request body
    // Declare an array
    const array = [];
    // Listen for the requested upload event and push the chunk data inside to the array, as the data may be uploaded bit by bit. So every time you upload a point, I'm going to push your point into this array
    request.on("data", chunk => {
      array.push(chunk);
    });
    // Listen for the end of the request, first to the array data into a string, this string is JSON syntax, because the request was set. Then turn the string into a JS object
    request.on("end", () = > {const string = Buffer.concat(array).toString();
      const obj = JSON.parse(string); {name password}
      })
    // Write to the database
      const lastUser = userArray[userArray.length - 1];
      const newUser = {  // New user information
        id: lastUser ? lastUser.id + 1 : 1.//id is the last user id+1 if the last user information exists, otherwise it is 1
        name: obj.name,  
        password: obj.password
      };
      userArray.push(newUser);
      fs.writeFileSync("./db/users.json".JSON.stringify(userArray));
    response.end();
 else {  // Otherwise the page is static
    response.statusCode = 200;
    // Default home page
    const filePath = path === "/" ? "/index.html" : path;
    const index = filePath.lastIndexOf(".");
    // suffix is a suffix
    const suffix = filePath.substring(index);
    const fileTypes = {
      ".html": "text/html".".css": "text/css".".js": "text/javascript".".png": "image/png".".jpg": "image/jpeg"
    };
    response.setHeader(
      "Content-Type".`${fileTypes[suffix] || "text/html"}; charset=utf-8`
    );
    let content;
    try {
      content = fs.readFileSync(`./public${filePath}`);
    } catch (error) {
      content = "File does not exist";
      response.statusCode = 404;
    }
    response.write(content);
    response.end();
  }

  /******** code ends, do not read ************/
});

server.listen(port);
console.log(
  "Listening" +
    port +
    "Success \n Please turn 720 degrees in the air and open http://localhost: with the rice cooker." +
    port
);

Copy the code

Goal 2: Login page

The effect

  1. The user submits the user name and password
  2. Run those two numbers against the database
  3. If yes, the user name and password are used, then the request is successful (return status code 200), skip to home page; If there is no match, the request fails (response status code 400).

Ideas (the first half of the same)

  1. Write a form in front and let the user fill in name and password
  2. The front end listens for the Submit event
  3. The front end sends a POST request with the data in the request body
  4. The back end receives the POST request
  5. The back end gets the name and password in the request body
  6. The back end compares the data to the data in the database

Specific steps

1. Write a form in front and let the user fill in name and password

  1. Create a new sign_in.html, this is the login page (go in directly, don’t set route, because static server)
  2. Write a form with an input of name and password and log in to button

2. The front end listens to the Submit event

  1. Reference to the jQuery
  2. Write JS directly in HTML with script tags
  3. Listen for submission events for form labels

3. The front end sends a POST request with data in the request body

When the form is submitted, the function needs to be executed

  • Prevents the default event of the form form

  • Get user data: find the element whose input name is name, take the user input value of this element, named name; Similarly, find the element whose input name is password, take the value entered by the user of this element, and name it password

  • POST request with jquery. ajax: To upload user data to the server, the server stores the data to the database

    (1) a POST request

    ② The requested URL is /sign_in

    ③ The data sent to the server is the JSON string with the user data just obtained in the request body

    ④ The request body type, that is, the data type sent to the server, is JSON

    Then set the request success function (jump to “/home.html” page) and failure function (do nothing).


      
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <! -- Mobile terminal please move to Taobao copy is done -->
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover"
    />
    <title>The login</title>
  </head>

  <body>
    <form id="signInForm">
      <div>
        <label>The user name<input type="text" name="name"/></label>
      </div>
      <div>
        <label>password<input type="password" name="password"/></label>
      </div>
      <div><button type="submit">The login</button></div>
    </form>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <script>
      const $form = $("#signInForm");
      $form.on("submit", e => {
        e.preventDefault();
        const name = $form.find("input[name=name]").val();
        const password = $form.find("input[name=password]").val();
        console.log(name, password);
        $.ajax({
          method: "POST".url: "/sign_in".contentType: "text/json; charset=UTF-8".data: JSON.stringify({ name, password })
        }).then(
          (a)= > {
            alert("Login successful");
            location.href = "/home"; }, () => {}); });</script>
  </body>
</html>

Copy the code

4. The backend receives the POST request

Write the corresponding route /sign_in, and also POST, to distinguish sign_in.html

Also set the route to /home/html

5. The backend obtains the name and password in the request body

6. The backend compares the data with the data in the database

Read the database first, then compare the data with the data in the database


 else if (path === "/sign_in" && method === "POST") {
    // Read the database
    const userArray = JSON.parse(fs.readFileSync("./db/users.json"));
    // Select name and password from the request body
    // Declare an array
    const array = [];
    // Listen for the requested upload event and push the chunk data inside to the array, as the data may be uploaded bit by bit. So every time you upload a point, I'm going to push your point into this array
    request.on("data", chunk => {
      array.push(chunk);
    });
    // Listen for the end of the request, first to the array data into a string, this string is JSON syntax, because the request was set. Then turn the string into a JS object
    request.on("end", () = > {const string = Buffer.concat(array).toString();
      const obj = JSON.parse(string); // Request body data: name password
      // Compare the data in the database
      // Select the same element from the database array as the requested body
      const user = userArray.find(
        user= > user.name === obj.name && user.password === obj.password
      );
      // The request fails if the status code is 404
      if (user === undefined) {
        response.statusCode = 400;
        response.setHeader("Content-Type"."text/json; charset=utf-8");
        response.end('{"errorCode":4001}')}else {  // Otherwise, the request succeeds with status code 200
        response.statusCode = 200;
        response.end()
      }
    })
  }
  else if (path === "/home"){
      response.end()
  }
Copy the code

Cookie

define

  • A Cookie is a string sent from the server to the browser
  • The browser must save the Cookie (unless the user deletes it)
  • Any subsequent request for the same secondary domain name (any request) must include a Cookie(in the header of the request)

Park tickets for comparison (draw)

  • If you’re a park ticket inspector, how do you know who’s allowed in and who’s not? You can get in with a ticket, you can’t get in without a ticket
  • Cookie is the ticket Cookie is logged in, no Cookie is not logged in that backend to the browser to send a Cookie is not done

Grammar see MDN

Pay attention to

  • Never set cookies on the front end. Only set cookies on the back endSet-Cookie: <cookie-name>=<cookie-value>; HttpOnlyThe front end can’t fiddle with cookies (otherwise the user can change them themselves)
  • Developer Tools ->Application->Cookie To view cookies

Goal 3: Use cookies to mark the user as logged in

The effect

  1. When the user logs in successfully from the login page, the home page will have: Welcome home
  2. However, if the user does not log in from the login page but enters the home page, the home page will read: not logged in

Train of thought

Specific steps

  1. Write /public/home. HTML, which will be the body of the home page /home

      
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <title>The home page</title>
  </head>
  <body>
    <p>{{loginStatus}}</p>
    <p><a href="sign_in.html">Please log in</a></p>
  </body>
</html>

Copy the code
  1. Send tickets at the moment the user name and password are correct – in the server /sign_in step 3, if the user name and password are correct, and indeed find a user in the database like him, set cookies immediately (give tickets), the ticket content is logined=1
response.setHeader("Set-Cookie", `logined=1; HttpOnly`)
Copy the code
  1. Set the route of home page /home.html, show logged in if there is a ticket, show not logged in if there is no ticket

 else if (path === "/sign_in" && method === "POST") {
    const userArray = JSON.parse(fs.readFileSync("./db/users.json"));
    const array = [];
    request.on("data", chunk => {
      array.push(chunk);
    });
    request.on("end", () = > {const string = Buffer.concat(array).toString();
      const obj = JSON.parse(string); 
      const user = userArray.find(
        user= > user.name === obj.name && user.password === obj.password
      );
      if (user === undefined) {
        response.statusCode = 400;
        response.setHeader("Content-Type"."text/json; charset=utf-8");
        response.end('{"errorCode":4001}')}else { 
        response.statusCode = 200;
        response.setHeader("Set-Cookie".`logined=1; HttpOnly`) // If the user name and password are correct, immediately give the ticket, that is, set the Cookie in the response header
        response.end()
      }
    })
  }
  else if (path === "/home.html") {const cookie = request.headers['cookie']  // Get the Cookie in the header of the request
      const homeHtml = fs.readFileSync("./public/home.html").toString();  // Get the contents of the file home.html and turn it into a string
      let x = cookie==='logined=1'?'Logged in':'Not logged in'  If cookie==='logined=1' is true, x is' logged in ', otherwise it is' not logged in '
      const string = homeHtml.replace('{{loginStatus}}'.`${x}`)  // Replace the placeholder in the string with x
      respose.write(string)  
  }
Copy the code

Goal 4: Record user.id with cookie

The effect

  1. When the user logs in successfully from the login page, the home page will have: XXX user, welcome home
  2. However, if the user does not log in from the login page but enters the home page, the home page will read: not logged in

Train of thought

Change the content of the cookie ticket to the id of the user found in the database, so that it can be passed along with the cookie. When entering the home page, there is a user ID, it shows logged in, replaced the placeholder, there is no is not logged in

Specific steps

  1. The ticket will be issued at the moment when the user name and password are correct — the third step in the server /sign_in compares the data in the database, if the user name and password are checked correctly and there is indeed that user in the database, the Cookie will be set immediately (to the ticket), the ticket content isuser_id=${user.id}The ID of that user in the database
response.setHeader("Set-Cookie", `user_id=${user.id}; HttpOnly`);

Copy the code
  1. In the /home page, find the login user Id in the cookie, if there is, then find the database with the same Id as his user, if there is, then the response body content is XXX user login; If you don’t have anything in front of it, it’s not coming in from the login page. The content of the response body and above is not logged in.
else if (path === "/home") {
    const cookie = request.headers["cookie"]; // Get the Cookie in the header of the request
    try {
      // try to extract the login userId from the cookie and name it userId
      userId = cookie
        .split(";")
        .filter(s= > s.indexOf("user_id") > =0) [0]
        .split("=") [1];
    } catch (error) {} // If it fails, fine
    console.log(userId);
    const homeHtml = fs.readFileSync("./public/home.html").toString(); // Get the contents of the file home.html and turn it into a string
    let string;
    // If the login user Id does exist from the cookie, then
    if (userId) {
      const userArray = JSON.parse(fs.readFileSync("./db/users.json")); // Read the database as the array userArray
      const user = userArray.find(user= > user.id.toString() === userId); // Find a user in the database whose ID is the same as that of the logged-in user
      if (user)
        // If the database does have a user with the same ID as the login user
        string = homeHtml
          .replace("{{loginStatus}}"."Logged in")
          .replace("{{user.name}}", user.name);
    } else {
      string = homeHtml
        .replace("{{loginStatus}}"."Not logged in")
        .replace("{{user.name}}"."");
    }
    response.write(string);
    response.end();
  } 
Copy the code

Security vulnerabilities

The content on the ticket (the user ID in the database we found) is equivalent to the customs clearance password, so anyone can use the developer tool (HttpOnly is set) or JS (HttpOnly is not set) to modify, if I know the database of Ma Huateng user ID, I can set my user_id= Ma Huateng ID, I can enter Ma Huateng’s home page

Goal 5: Tamper-proof user.id

Idea 1: encryption

  • The user_id is encrypted and sent to the front end, and decrypted when the back end reads the user_id.
  • It works, but there are security holes
  • Vulnerability: Encrypted content can be used indefinitely
  • Solution: JWT (later)

Session! Hide the information on the server

  • Put the user information into the server’s session and give the information a random ID

  • Send a random ID to the browser

  • The backend obtains user information through session[ID] the next time it reads the ID

  • Think about why users can’t tamper with ids (because they are long and random)

  • A session is a file. Can’t use memory because it will be empty if the power goes out

  • The cookie sent by the server to the browser contains a random number saved by the server, rather than the real user ID. This random number corresponds to the real user ID in the session file of the server, and only the server knows which ID this string of random numbers corresponds to.

  • The server uses random numbers to store user ids, and uses random numbers to find user ids. And the browser only knows random numbers

Parse (fs.readfilesync ()) const session = JSON. Parse (fs.readfilesync ())"./session.json").toString());

  console.log("There's a request from some idiot! The path (with query parameters) is:" + pathWithQuery);

  if (path === "/sign_in" && method === "POST") {
    const userArray = JSON.parse(fs.readFileSync("./db/users.json"));
    const array = [];
    request.on("data", chunk => {
      array.push(chunk);
    });
    request.on("end", () => { const string = Buffer.concat(array).toString(); const obj = JSON.parse(string); // Request body data: name password const user = userArray.find( user => user.name === obj.name && user.password === obj.password );if (user === undefined) {
        response.statusCode = 400;
        response.setHeader("Content-Type"."text/json; charset=utf-8");
        response.end('{"errorCode":4001}');
      } elseResponse.statuscode = 200; response.statusCode = 200; response.statusCode = 200; response.end(); const random = Math.random(); Session [random] = {user_id: user.id}; // Add a new attribute to the session object. The name of the attribute is the random number. The value of the attribute is {user_id: the id of the user found in the database} fs.writefilesync ("./session.json", JSON.stringify(session)); Response.setheader (response.setheader)"Set-Cookie", `session_id=${random}; HttpOnly`); // Session_id = session_id= session_id= session_id= session_id= session_id= session_id= session_id= session_id= session_id= session_id= session_id }); }else if (path === "/home.html") {
    
    const cookie = request.headers["cookie"]; // Get the Cookie in the requestletsessionId; Try {// Try: extract random number from ticket contents. Call it sessionId (the browser can only give me random numbers because I only gave it random numbers) sessionId = cookie.split (";")
        .filter(s => s.indexOf("session_id=") >= 0)[0]
        .split("=") [1]; } catch (error) {}if(sessionId && Session [sessionId]) {// If there is a random number in the cookie and I go to my session object, Const userId = session[sessionId]. User_id; const userId = session[sessionId]. Parse (fs.readfilesync ()) const userArray = json. parse(fs.readfilesync ("./db/users.json")); const user = userArray.find(user => user.id === userId); Const homeHtml = fs.readfilesync () const homeHtml = fs.readfilesync ()"./public/home.html").toString(); /
      let string = "";
      if (user) {
        string = homeHtml
          .replace("{{loginStatus}}"."Logged in")
          .replace("{{user.name}}", user.name);
      }
      response.write(string);
    } else {
      const homeHtml = fs.readFileSync("./public/home.html").toString();
      const string = homeHtml
        .replace("{{loginStatus}}"."Not logged in")
        .replace("{{user.name}}"."");
      response.write(string);
    }
    response.end();
  } 
Copy the code

Cookie/Session summary

  1. The server can send cookies to the browser
  • Through the Response Header
  • See MDN for the syntax
  1. Cookies on your browser can be tampered with
  • You can change it using developer tools
  • Cookies sent from the weak backend can be tampered with with JS (because HttpOnly is not written)
  1. So, the server needs to send cookies that can be tampered with, but that would have no real effect
  • Cookies can contain encrypted information (and have to be decrypted, trouble)
  • Session: Cookies can also contain a single random id. Session [random id] can be used to retrieve the corresponding information on the back end. This ID cannot be tampered with (but can be copied, which is not a problem).

Goal 6: Log off

Train of thought

  1. Delete session[random id] from session
  2. Delete the cookie from the browser (optional)

implementation

  1. Front end make logout button
  2. The front end listens for the logout button click event
  3. The front-end sends a DELETE Session request
  4. The back end accepts the DELETE Session request
  5. The backend retrieves the current session[random number ID] and deletes it
  6. The back end issues an expired Cookie with the same name (because it was not deleted)
  • In addition to deleting the cookies on the browser, delete the corresponding Session data
  • For security reasons, do not delete cookies with JS. Instead, use HTTP only cookies. Then, JS sends a request to the server to delete cookies

Bug: User passwords are leaked

Goal 7: Prevent password leakage

  1. Don’t store plaintext
  • After getting the plaintext, bcrypt.js encrypts and gets the ciphertext
  • Save the ciphertext to users.json
  • When a user logs in, the encrypted text is compared to whether the ciphertext is one-to-one
  • If they are consistent, the user knows the password and can log in
  1. Don’t use MD5
  • Fool uses MD5 to process password
  • MD5 isn’t even an encryption algorithm, it’s a hash algorithm
  • Don’t be misled by the spicy chicken tutorial
  • Read more on this blog

Great summary

  1. How to get the body of a POST request (back-end knowledge)
  2. How to use Cookies (back-end knowledge)
  • Never use JS to manipulate cookies, http-only
  1. What is Session (Back-end knowledge)
  • A file on the back end that stores session data, usually user information
  1. Implementation of registration, login, and logout (backend)
  • Every Web programmer should know this