Introduction to the

  • As HTTP is a stateless protocol, cookies, session, and JWT are generally used for identity authentication to save user login status and distinguish users
  • Cookie is in http-header (should not be too large to reduce the amount of data transmitted)
  • Cookie can be added through the browser, the server can also set cookies, each request will carry cookies (each request carries, wasting traffic) reasonable Settings
  • Cookie default can not cross domain (two completely different domain name father and son domain name (can set the sub-domain can get the data of the parent domain), cookie exists in the front end
  • If the session is in the server, the default browser can’t get it. The session can store data. In principle, the session is not online, and it is safe.
  • In the JWT scenario, the service generates a token based on the information provided by the user. Each time bring the token and your information, use your information to generate the token again for comparison (no privacy can be stored in it)

cookie

The characteristics of

  • Only string values can be stored
  • Generally, a single Cookie cannot store more than 4K of data
  • Information held temporarily or permanently by a client computer
  • Each time the browser requests the server, it carries the cookie under the current domain name by default and puts it in the HTTP request header. Because the cookie is carried in each request, it should be set appropriately to reduce traffic waste
  • Since it is stored in a browser and can be tampered with, you can add signature verification to improve security
  • Saved as a string in the browser;Js is obtained through document.cookie, and some options can be added
  • The XSRF attack exploits the feature that browser requests carry cookies automatically

options

  • nameIndicates the name of the cookie. The cookie names in a domain name cannot be the same because they will be overwritten
  • valueRepresents the value of the cookie
  • domainThe cookie bound domain name, if not set, is automatically bound to the current domain where the statement is executed. You can set this property for the parent domain name to implement reading and writing of different subdomains
  • pathIf the path is set to/ABC, only routes under/ABC can access the cookie, for example, / ABC /read. The default is/Usually not set, if set is very limited
  • secureWhen secure is true, cookies are not valid for HTTP, but for HTTPS.
  • expires/max-ageSurvival time
  • httpOnlyIf the httpOnly attribute is set for a cookie, the cookie information cannot be read through THE JS script, but the cookie can be manually modified through the Application, so it can only prevent XSS attacks to a certain extent, not absolute security

Set cookies in node native

 res.setHeader('Set-Cookie'['name=zf'.'age=12; domain=.zf.cn; httpOnly=true']);
Copy the code

Use cookies in KOA

Implementing cookie middleware
// Delete =, convert + to -, and/to _
const toBase64URL = (str) = > {
    return str.replace(/=/g."").replace(/\+/g."-").replace(/ / / /."_");
};
app.use(async (ctx, next) => {
    // Save the cookie to be sent
    const cookies = [];
    ctx.myCke = {
        // Method of setting cookies: key:cookie name value:cookie name Options: other configurations, such as httpOnly/domain/max-age
        set(key, value, options = {}) {
            // An array of other configurations
            let optsArr = [];
            // If domain is set, add this configuration
            if (options.domain) {
                optsArr.push(`domain=${options.domain}`);
            }
            if (options.httpOnly) {
                optsArr.push(`httpOnly=${options.httpOnly}`);
            }
            if (options.maxAge) {
                optsArr.push(`max-age=${options.maxAge}`);
            }
            // Encrypted signatures are configured
            if (options.signed) { 
                // Base64 handles + / = special when transmitting
                  const salt = [key, value].join("=");
                let sign = toBase64URL(crypto.createHmac("sha1", secret).update(salt).digest("base64"));
                cookies.push(`${key}.sign=${sign}`);
            }
            // Put the cookie configuration into cookies
            cookies.push(`${key}=${value}; ${optsArr.join("; ")}`);
            console.log(cookies);
            // Set the response header to return cookies
            ctx.res.setHeader("Set-Cookie", cookies);
        },
        get(key, options) {
            // Get the cookie from the browser and parse it into an object format
            let cookieObj = querystring.parse(ctx.req.headers["cookie"]."; "); // a=1; b=2 {a:1,b:2}
            // If you need to verify the signature
            if (options.signed) {
                // Compares the signature carried in the cookie with the real signature. If the signature is the same, the cookie is returned
                if (cookieObj[`${key}.sign`] === toBase64URL(crypto.createHmac("sha1", secret).update(`${key}=${cookieObj[key]}`).digest("base64"))) {
                    return cookieObj[key];
                } else {
                    return "error"; }}return cookieObj[key] || ""; }};return next();
});
Copy the code
Use built-in cookies
const secret = "secret";
router.get("/write".async function (ctx) {
    ctx.cookies.set("name"."zf", {
        httpOnly: true
    });
    ctx.cookies.set("age"."12", {
        signed: true
    });
    ctx.body = "write ok";
});
router.get("/read".async function (ctx) {
    ctx.body = ctx.cookies.get("age", {
        signed: true}) | |"empty"; // name=zf; age=12
});
Copy the code

session

The characteristics of

  • The storage type is rich, and the amount of data stored is much larger than cookie
  • Save it on the server, which is more secure
  • It is stored on a server. However, too many accesses occupy too many server resources, affecting server performance
  • Cookie based, carries the sessionId through the cookie
  • Sessions are stored in content by default, and if the server restarts, sessions are lost

The principle of

let cardName = "zf"; // Store name
let session = {}; // Session is a server bookkeeping book that can be used to find specific information later
/ / get sessionId
 let hasVisit = ctx.cookies.get(cardName,{signed:true});
 // You must make sure that your card is from my store
    if(hasVisit && session[hasVisit]){ 
        session[hasVisit].mny -= 100;
        ctx.body = 'Congratulations on your purchase.' + session[hasVisit].mny
    }else{
        const id = uuid.v4(); / / 500
        session[id] = {mny:500};
        ctx.cookies.set(cardName,id,{signed:true});
        ctx.body = 'Congratulations, you're a member of the store. $500.'
    }
Copy the code

Use koa – the session

let cardName = "zf";
router.get("/wash".async function(ctx) {
    let hasVisit = ctx.session[cardName];
    if (hasVisit) {
        ctx.session[cardName].mny -= 100;
        ctx.body = "Congratulations on your purchase." + ctx.session[cardName].mny;
    } else {
        ctx.session[cardName] = { mny: 500 };
        ctx.body = "Congratulations, you're a member of the store. $500."; }});Copy the code

Just close the browser and the session really disappears, right?

  • Not right. In the case of sessions, the server will remain in place until the application notifies the server to delete a session. Generally, the application sends a command to delete a session when the user logs off. However, the browser never actively notifies the server that it is closing before closing, so the server never has a chance to know that the browser is closing
  • The reason for this illusion is that most session mechanisms use session cookies to store the session ID, which disappears when the browser is closed and the original session cannot be found when the server is connected again.
  • If the cookie set by the server is saved on the hard disk, or the HTTP request header sent by the browser is rewritten in some way to send the original session ID to the server, the browser can still open the original session again.
  • It is precisely because closing the browser does not cause the session to be deleted that the server sets an expiration date for the session. When the client last used the session, the server assumes that the client has ceased to be active. Delete session to save storage space.

jwt

The composition of JWT

The three parts of JWT are as follows.

  • Header
    • The Header section is a JSON object that describes the metadata of the JWT, usually something like this.
    {
      "alg": "HS256"."typ": "JWT"
    }
    Copy the code
    • In the above code, the ALG attribute represents the algorithm of the signature. The default is HMAC SHA256 (written as HS256). The TYp attribute represents the type of the token, and the JWT token is written as JWT.
    • Finally, convert the above JSON object into a string using the Base64URL.
  • Payload
    • The Payload part is also a JSON object that stores the data that needs to be transmitted. JWT specifies seven official fields to choose from.
    • issIssuer: indicates the issuer
    • expExpiration time: indicates the expiration time
    • sub(a subject) : theme
    • aud(on) : the audience
    • nbf(Not Before) : Effective time
    • iatIssued At: Time of issuance
    • jti(JWT ID) : indicates the number
  • Signature (= Signature)
    • The Signature section is a Signature to the first two sections, preventing data tampering.
  • Header.payload-signature
  • Base64URL

JWT as a token may be placed in a URL in some cases (such as api.example.com/?token=xxx). Base64 has three characters +, /, and =, which have special meanings in URLS and are therefore replaced: = is omitted, + is replaced with -, and/is replaced with _. This is the Base64URL algorithm.

The characteristics of

  • Stateless server and good scalability
  • The browser does not carry tokens by default. Therefore, the tokens need to be included in the Authorization header for each request, which reduces data transmission
  • You can bypass the browser’s same-origin policy, making it easier to carry credentials across domains
  • Token-based authentication is stateless on the server. The server uses the time calculated by token to obtain storage space on the server
  • Supports mobile devices
  • The default is not encrypted, but it can be encrypted. Once the original Token is generated, it can be encrypted again with the key.
  • Secret data cannot be written to the JWT without encryption.
  • JWT can be used not only for authentication, but also for information exchange. Using JWT effectively can reduce the number of times the server queries the database.
  • The biggest drawback of JWT is that since the server does not store session state, there is no way to invalidate a token or change the token’s permissions during use. That is, once a JWT is issued, it remains valid until expiration, unless the server deploys additional logic.
  • The JWT itself contains authentication information, and if it is disclosed, anyone can gain full access to the token. To reduce theft, JWT expiration dates should be shorter. For some important permissions, the user should be authenticated again.
  • To reduce theft, JWT should use HTTPS instead of HTTP.

Implement the JWT principle

const secret = "zf"; / / the secret key
const jwt = {
    // encrypt through the crypto module, return base64 and handle the = + / in base64
    sign(content, secret) {
        return this.toBase64URL(crypto.createHmac("sha256", secret).update(content).digest("base64"));
    },
    // Handles the = + / of strings in base64
    toBase64URL: (str) = > {
        return str.replace(/=/g."").replace(/\+/g."-").replace(/ / / /."_");
    },
    // Decrypt the fixed form
    base64urlUnescape(str) {
        str += new Array(5 - str.length % 4).join("=");
        return str.replace(/-/g."+").replace(/_/g."/");
    },
    // Convert the content to base64
    toBase64(content) {
        return this.toBase64URL(Buffer.from(JSON.stringify(content)).toString("base64"));
    },
    // Returns the token based on the content and the secret key
    encode(info, secret) {
        // Head is a fixed object converted to JSON for encryption
        const head = this.toBase64({
            typ: "JWT".alg: "HS256"
        });
        // Content is base64 conversion of incoming content
        const content = this.toBase64(info);
        // sign encrypts head and content
        const sign = this.sign([head, ".", content].join(""), secret);
        return head + "." + content + "." + sign;
    },
    // Decrypt operation
    decode(token, secret) {
        // Get the three parts of the token string
        let [head, content, sign] = token.split(".");
        // Re-encrypt token's head and content, compare with sign, if same, prove not tampered
        let newSign = this.sign([head, content].join("."), secret);
        if (newSign === sign) {
            // Decrypt the content back
            return JSON.parse(Buffer.from(this.base64urlUnescape(content), "base64").toString());
        } else {
            throw new Error("User has changed information"); }}};Copy the code