The project management

Multi-environment Configuration

  • JSON configuration file
  • Environment variables managed using third-party modules (NCONF)

Dependency management

  • Dependencies: Dependencies required for the normal operation of a module
  • DevDependencies: Dependencies needed at development time
  • OptionalDependencies: optionalDependencies, enhanced to some extent
  • PeerDependencies: Runtime dependencies, limited version

Exception handling

Handle uncaught exceptions

Unless the developer remembers to add a. Catch statement, errors thrown in these places will not be handled by the uncaughtException event handler and then disappear.

Node applications do not crash, but may leak memory

Process. on('uncaughtException', (error) => {// I just received an error that has never been handled // Now handle it, And decide whether need to restart the application errorManagement. Handler. HandleError (error); if (! errorManagement.handler.isTrustedError(error)) { process.exit(1); }}); Process. on('unhandledRejection', (reason, p) => {// I just captured an unprocessed Promise rejection, // Because we already have a back-up handling mechanism for unhandled errors (see below) // Throw reason directly; });Copy the code

The domain management is abnormal

  • Create the instance through the Create method of the Domain module
  • An error and any other errors are handled by the same error handler
  • Any code that causes an error in this callback will be overwritten by the domain
  • Allows our code to run in a sandbox and use res objects to give feedback to the user

    const domain = require(‘domain’); const audioDomain = domain.create();

    audioDomain.on(‘error’, function (err) { console.log(‘audioDomain error:’, err); });

    audioDomain.run(function () { const musicPlayer = new MusicPlayer(); musicPlayer.play(); });

Joi validates parameters

Const memberSchema = joi.object ().keys({password: joi.string ().regex(/^[a-za-z0-9]{3,30}$/), birthyear: Joi.number().integer().min(1900).max(2013), email: Joi.string().email(), }); function addNewMember(newMember) { //assertions come first Joi.assert(newMember, memberSchema); //throws if validation fails //other logic here }Copy the code

Kibana system monitoring

See this article github.com/goldbergyon…

Online practice

Use Winston to keep a journal

var winston = require('winston'); var moment = require('moment'); const logger = new(winston.Logger)({ transports: [ new(winston.transports.Console)({ timestamp: function () { return moment().format('YYYY-MM-DD HH:mm:ss') }, formatter: Function (params) {let time = params.timestamp() // let message = params.message // Object.keys(params.meta).length ? '\n\t' + JSON.stringify( params.meta) : '' return `${time} ${message}` }, }), new(winston.transports.File)({ filename: `${__dirname}/../winston/winston.log`, json: false, timestamp: function () { return moment().format('YYYY-MM-DD HH:mm:ss') }, formatter: Function (params) {let time = params.timestamp() // let message = params.message // Object.keys(params.meta).length ? '\n\t' + JSON.stringify( params.meta) : '' return `${time} ${message}` } }) ] }) module.exports = logger // logger.error('error') // logger.warm('warm') // logger.info('info')Copy the code

Entrusted reverse agent

Node performs poorly in processing CPU-intensive tasks, such as gzipping and SSL termination. Instead, it is better to use a true middleware service like Nginx. Otherwise the poor single-threaded Node will unfortunately be busy with network tasks instead of the application core, and performance will suffer accordingly.

Express.js handles static files through some connect middleware, but you shouldn’t use it. Nginx handles static files better and prevents requests for dynamic content from clogging our Node process.

Gzip on; gzip_comp_level 6; gzip_vary on; Upstream myApplication {server 127.0.0.1:3000; Server 127.0.0.1:3001; keepalive 64; } # configure server with SSL and error pages listen 80; listen 443 ssl; ssl_certificate /some/location/sillyfacesociety.com.bundle.crt; error_page 502 /errors/502.html; # handling static content location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) { root /usr/local/silly_face_society/node/public; access_log off; expires max; }Copy the code

Detect vulnerable dependencies

docs.npmjs.com/cli/audit

PM2 HTTP cluster configuration

Worker thread configuration

  • Js -i 4, -i 4 runs the app in Cluster_mode and has four worker threads. If 0 is configured, Pm2 generates the corresponding worker threads based on the number of CPU cores
  • If a worker thread dies, PM2 restarts immediately. PM2 Scale Extends the cluster

The PM2 starts automatically

  • Pm2 Save Saves the current running application
  • Pm2 startup start

Performance practices

Avoid Lodash

  • Using a method library like LoDash leads to unnecessary dependencies and slower performance
  • With the introduction of the new V8 engine and the new ES standard, the native method has been improved and the performance analogy library is now 50% higher using the ESLint plugin:

    { “extends”: [ “plugin:you-dont-need-lodash-underscore/compatible” ] }

benchmark

const _ = require('lodash'),
    __ = require('underscore'),
    Suite = require('benchmark').Suite,
    opts = require('./utils');
//cf. https://github.com/Berkmann18/NativeVsUtils/blob/master/utils.js

const concatSuite = new Suite('concat', opts);
const array = [0, 1, 2];

concatSuite.add('lodash', () => _.concat(array, 3, 4, 5))
    .add('underscore', () => __.concat(array, 3, 4, 5))
    .add('native', () => array.concat(3, 4, 5))
    .run({
        'async': true
    });
Copy the code

Use Prof for performance analysis

Use the Tick-Processor to process the analysis

node --prof profile-test.js

npm install tick -gnode-tick-processor
Copy the code

Use headdump heap snapshot

  • Code loading module for snapshot file generation
  • Chrome Profiles loads snapshot files

    yarn add heapdump -D

    const heapdump = require(‘heapdump’); const string = ‘1 string to rule them all’;

    const leakyArr = []; let count = 2; setInterval(function () { leakyArr.push(string.replace(/1/g, count++)); }, 0);

    setInterval(function () { if (heapdump.writeSnapshot()) console.log(‘wrote snapshot’); }, 20000);

Application Safety List

Helmet sets the security response header

Check the head configuration: Security Headers

Applications should use secure headers to prevent attackers from using common attacks such as cross-site scripting (XSS) and cross-site request forgery (CSRF). You can easily configure it using the module Helmet.

  • Constructor: x-frame-options: sameOrigin Provides click jacking protection, iframe can only be homologous.

  • Transmission: strict-transport-security: max-age=31536000; IncludeSubDomains. Enforce HTTPS, which reduces errors in Web applications through cookies and external links, leaks session data, and prevents man-in-the-middle attacks

  • X-content-type-options: nosniffing. Prevents response sniffing from the declared Content Type, reducing the risk caused by users uploading malicious Content. Charset = utf-8. Instructs the browser to interpret the page as a specific content type, rather than relying on the browser to make assumptions

  • XSS: x-xss-protection: 1; Mode = block. Cross-site scripting (XSS) filters built into the latest Web browsers are enabled

  • X-download-options: noopen.

  • Cache: cache-control: no-cache. Data returned from the Web application can be cached by the user’s browser as well as by an intermediate proxy. This directive instructs them not to keep page content so that others can access sensitive content Pragma: no-cache from these caches. Same as above Expires: -1. The data returned in the Web response can be cached by the user’s browser as well as by an intermediate proxy. The directive prevents this by setting the expiration time to a value.

  • Access-control-allow-origin: not *. ‘access-Control-allow-origin: *’ by default x-permitted – cross-domain-policies: master-only is disabled in modern browsers. Indicates that only the specified files are considered valid in this domain

= Content Security Policy: content-security-policy: Content Security policies need to be carefully adjusted and defined

  • Server information: Server: not displayed.

Use the security-linter plugin

Use the security checking plugin eslint-plugin-security or tslint-config-security.

Koa-ratelimit limits concurrent requests

DOS attacks are popular and relatively easy to deal with. Use external services such as Cloud load balancing, Cloud firewall, Nginx, or (for small, less important apps) a rate-limiting middleware such as Koa-Ratelimit to implement rate limiting.

Plain text confidential information placement

Confidential information stored in source control must be encrypted and managed (rolling keys, expiration times, reviews, etc.). Use pre-commit/push hooks to prevent accidental submission of confidential information.

ORM/ODM libraries protect against query injection vulnerabilities

To protect against SQL/NoSQL injection and other malicious attacks, always use ORM/ODM or Database libraries to escape data or support named or indexed parameterized queries, and take care to validate the expected type of user input. Do not just use JavaScript template strings or string concatenations to insert values into queries, as this exposes the application to a wide range of vulnerabilities.

Library:

  • TypeORM
  • sequelize
  • mongoose
  • Knex
  • Objection.js
  • waterline

Use Bcrypt instead of Crypto

Passwords or confidential information (API keys) should be stored using the secure Hash + salt function (bcrypt), which should be the preferred JavaScript implementation for performance and security reasons.

Bcrypt. hash('myPassword', 10, function (err, hash) {// Store the secure hash in the user record}); Bcrypt.com pare('somePassword', hash, function (err, Match) {if (match) {// password matches} else {// password does not match}});Copy the code

Escape HTML, JS, and CSS output

Untrusted data sent to the browser may be executed instead of displayed, which is often referred to as a cross-site scripting (XSS) attack. This problem can be mitigated by using dedicated libraries to explicitly mark data as plain text content (e.g., encoding, escaping) that should not be performed.

Validate the incoming JSON Schemas

Verify the incoming request’s body payload and make sure it meets the expected requirements, or quickly report an error if it does not. To avoid tedious validation encoding in each route, you can use a lightweight JSON-based validation schema, such as JSON Schema or JOI

Support for blacklisted JWT

When using JSON Web Tokens(for example, through Passport. Js), by default there is no mechanism to revoke access from issued Tokens. Once some malicious user activity is detected, as long as they hold valid tags, they cannot be prevented from accessing the system. This problem is mitigated by implementing a blacklist of untrusted tokens and validating on each request.

const jwt = require('express-jwt');
const blacklist = require('express-jwt-blacklist');

app.use(jwt({
    secret: 'my-secret',
    isRevoked: blacklist.isRevoked
}));

app.get('/logout', function (req, res) {
    blacklist.revoke(req.user)
    res.sendStatus(200);
});
Copy the code

Limit the number of login requests allowed per user

A class of brute protection middleware, such as Express-Brute, should be used in Express applications to prevent brute/dictionary attacks; This type of attack is mainly applied to sensitive routes, such as /admin or /login, based on certain request attributes, such as user names, or other identifiers, such as body parameters. Otherwise an attacker can issue unlimited password matching attempts to gain access to a privileged account in the application.

const ExpressBrute = require('express-brute'); const RedisStore = require('express-brute-redis'); Const redisStore = new redisStore ({host: '127.0.0.1', port: 6379}); // Start slowing requests after 5 failed // attempts to login for the same user const loginBruteforce = new ExpressBrute(redisStore, { freeRetries: 5, minWait: 5 * 60 * 1000, // 5 minutes maxWait: 60 * 60 * 1000, // 1 hour failCallback: failCallback, handleStoreError: handleStoreErrorCallback }); app.post('/login', loginBruteforce.getMiddleware({ key: function (req, res, next) { // prevent too many attempts for the same username next(req.body.username); } }), // error 403 if we hit this route too often function (req, res, next) { if (User.isValidLogin(req.body.username, req.body.password)) { // reset the failure counter for valid login req.brute.reset(function () { res.redirect('/'); // logged in }); } else { // handle invalid user } } );Copy the code

Run Node.js as a non-root user

It is a common scenario for Node.js to run as a root user with unlimited privileges. For example, in a Docker container, this is the default behavior. It is recommended to create a non-root user and save it to the Docker image (as shown below), or to run the process on behalf of this user by calling a container with “-u username”. Otherwise the attacker running the script on the server gains unlimited rights on the local machine (for example, changing the IPtable to divert traffic to his server)

FROM node:latestCOPY package.json .RUN npm installCOPY . .EXPOSE 3000USER nodeCMD ["node", "server.js"]
Copy the code

Use reverse proxies or middleware to limit the load size

The larger the request body payload, the harder it is for a single thread of Node.js to process it. This is an opportunity for the attacker to bring the server to its knees without a lot of requests (DOS/DDOS attacks). This problem can be mitigated by limiting the body size of incoming requests on the edge (for example, firewall, ELB) or by configuring Express Body Parser to receive only small payloads. Otherwise your application will have to process large requests and not be able to handle the other important work it must do, resulting in performance impact and vulnerability to DOS attacks.

express

const express = require('express'); const app = express(); // body-parser defaults to a body size limit of 300kb app.use(express.json({ limit: '300kb' })); // Request with json body app.post('/json', (req, res) => { // Check if request payload content-type matches json // because body-parser does not check for content types if (! req.is('json')) { return res.sendStatus(415); // Unsupported media type if request doesn't have JSON body } res.send('Hooray, it worked! '); }); app.listen(3000, () => console.log('Example app listening on port 3000! '));Copy the code

Nginx:

http {
    ...
    # Limit the body size for ALL incoming requests to 1 MB
    client_max_body_size 1m;
}

server {
    ...
    # Limit the body size for incoming requests to this specific server block to 1 MB
    client_max_body_size 1m;
}

location /upload {
    ...
    # Limit the body size for incoming requests to this route to 1 MB
    client_max_body_size 1m;
}
Copy the code

Prevent RegEx from overloading NodeJS

User input that matches text requires a lot of CPU cycles to process. In some ways, the re processing is inefficient, such as validating a single request of 10 words that might block the entire Event loop for up to 6 seconds. For this reason, favor third-party validation packages such as validator.js over regees, or safe-regex to detect problematic regular expressions.

const saferegex = require('safe-regex'); const emailRegex = /^([a-zA-Z0-9])(([\-.]|[_]+)? ([9] a - zA - Z0 - +)) * (@) {1} [a - Z0-9] + [.] {1} (([a-z] {2, 3}) | ([a-z] {2, 3} {1} [.] [a-z] {2, 3})) $/; // should output false because the emailRegex is vulnerable to redos attacks console.log(saferegex(emailRegex)); // instead of the regex pattern, use validator: const validator = require('validator'); console.log(validator.isEmail('[email protected]'));Copy the code

Running unsafe code in a sandbox

When tasks execute external code given at runtime (for example, plug-ins), use any type of sandbox execution environment to protect the main code and separate the main code from the plug-ins. This can be done through a dedicated process (for example :cluster.fork()), a serverless environment, or a dedicated NPM package that acts as a sandbox.

  • A dedicated child process – this provides a quick isolation of information, but requires constraints on the child process, limiting its execution time, and recovering from errors
  • A cloud-based no-service framework satisfies all sandbox requirements, but dynamic deployment and invocation of Faas methods are not the subject of this section
  • Some NPM libraries, such as Sandbox and VM2, allow isolated code to be executed with a single line of code. Although the latter option wins in simplicity, it offers limited protection.

    const Sandbox = require(“sandbox”); const s = new Sandbox();

    s.run(“lol)hai”, function (output) { console.log(output); //output=’Synatx error’ });

    // Example 4 – Restricted code s.run(“process.platform”, function (output) { console.log(output); //output=Null })

    // Example 5 – Infinite loop s.run(“while (true) {}”, function (output) { console.log(output); //output=’Timeout’ })

Hide client error details

By default, the integrated Express error handler hides error details. Most likely, however, you implement your own error handling logic with custom error objects (considered best practice by many). If you do, make sure that the entire Error object is not returned to the client, which may contain sensitive application details. Otherwise sensitive application details, such as server file paths, third-party modules in use, and other internal workflows of the application that could be exploited by an attacker, could be leaked from stack Trace’s discovery.

// production error handler
 // no stacktraces leaked to user
 app.use(function (err, req, res, next) {
     res.status(err.status || 500);
     res.render('error', {
         message: err.message,
         error: {}
     });
 });
Copy the code

For NPM or Yarn, configure 2FA

Any step in the development chain should be protected with MFA(Multiple Authentication), and NPM /Yarn is a great opportunity for attackers who can get their hands on some developer passwords. Using developer credentials, an attacker can inject malicious code into libraries that are widely installed across projects and services. Maybe even publicly posted on the web. With two-layer authentication (2-factor-authentication) enabled in NPM, attackers have little chance of changing your package code.

Session Middleware Setup

Every Web framework and technology has its known weaknesses, and telling an attacker what web framework we’re using is a big help. Using the default Settings of the Session middleware, you can expose your application to module – and frame-specific hijacking attacks in a manner similar to x-powered-ByHeader. Try to hide anything that identifies and uncovers the technology stack (e.g. Nonde.js, Express). Otherwise cookies could be sent over an insecure connection, and attackers could use session identifiers to identify the web application infrastructure and module-specific vulnerabilities.

// using the express session middleware
app.use(session({
    secret: 'youruniquesecret', // secret string used in the signing of the session ID that is stored in the cookie
    name: 'youruniquename', // set a unique name to remove the default connect.sid
    cookie: {
        httpOnly: true, // minimize risk of XSS attacks by restricting the client from reading the cookie
        secure: true, // only send cookie over https
        maxAge: 60000 * 60 * 24 // set cookie expiry length in ms
    }
}));
Copy the code

Csurf prevent CSRF

The routing layer:

var cookieParser = require('cookie-parser'); var csrf = require('csurf'); var bodyParser = require('body-parser'); var express = require('express'); Var csrfProtection = CSRF ({cookie: true}); var parseForm = bodyParser.urlencoded({ extended: false }); var app = express(); // We need this because "cookie" is correct in csrfProtection app.use(cookieParser()); App.get ('/form', csrfProtection, function (req, res) {// Render ('send', {CSRFToken: req.csrfToken() }); }); app.post('/process', parseForm, csrfProtection, function (req, res) { res.send('data is being processed'); });Copy the code

Presentation layer:

  <form action="/process" method="POST">    <input type="hidden" name="_csrf" value="{{csrfToken}}">  Favorite color: <input type="text" name="favoriteColor">  <button type="submit">Submit</button></form>  
Copy the code

Integrated application of

Watch service

const fs = require('fs');
const exec = require('child_process').exec;

function watch() {
    const child = exec('node server.js');
    const watcher = fs.watch(__dirname + '/server.js', function () {
        console.log('File changed, reloading.');
        child.kill();
        watcher.close();
        watch();
    });
}

watch();
Copy the code

RESTful web application

  • REST means representational state transfer
  • Use the right HTTP methods, URLs, and headers to create semantic RESTful apis
  • GET /gages: GET
  • POST /pages: created
  • GET /pages/10: Gets Pages10
  • PATCH/Pages /10: Updates pages10
  • PUT /pages/10: Replaces pages10
  • DELETE /pages/10: DELETE pages10

    let app; const express = require(‘express’); const routes = require(‘./routes’);

    module.exports = app = express();

    app.use(express.json()); // Use JSON body to parse app.use(express.methodoverride ()); // Allows a query parameter to specify additional HTTP methods

    App.get (‘/pages’, routes.pages. Index); app.get(‘/pages/:id’, routes.pages.show); app.post(‘/pages’, routes.pages.create); app.patch(‘/pages/:id’, routes.pages.patch); app.put(‘/pages/:id’, routes.pages.update); app.del(‘/pages/:id’, routes.pages.remove);

Middleware application

const express = require('express'); const app = express(); const Schema = require('validate'); const xml2json = require('xml2json'); const util = require('util'); const Page = new Schema(); Page.path('title').type('string').required(); Function ValidatorError(errors) {this.statusCode = 400; this.message = errors.join(', '); } util.inherits(ValidatorError, Error); Function xmlMiddleware(req, res, next) {// If (! req.is('xml')) return next(); let body = ''; Req. on('data', function (STR) {// Body += STR; }); req.on('end', function () { req.body = xml2json.toJson(body.toString(), { object: true, sanitize: false, }); next(); }); } function checkValidXml(req, res, next) {const page = page.validate (req.body.page); if (page.errors.length) { next(new ValidatorError(page.errors)); // Pass an error to next to prevent the route from continuing} else {next(); }} function errorHandler(err, req, res, next) {console.error('errorHandler', err); res.send(err.statusCode || 500, err.message); } app.use(xmlMiddleware); // Apply XML middleware to all requests app.post('/pages', checkValidXml, function (req, res) {// Apply XML console.log('Valid Page :', req.body.page); res.send(req.body); }); app.use(errorHandler); // Add error-handling middleware app.listen(3000);Copy the code

Organize applications by events

Const express = require('express'); const app = express(); const emails = require('./emails'); const routes = require('./routes'); app.use(express.json()); app.post('/users', routes.users.create); // Set the route to create user app.on('user:created', emails. Welcome); Module.exports = app; // exports = 0; // exports = 0; // exports = 0; Const User = require('./.. /models/user'); module.exports.create = function (req, res, next) { const user = new User(req.body); user.save(function (err) { if (err) return next(err); res.app.emit('user:created', user); Res.send ('User created'); }); };Copy the code

WebSocket and session

const express = require('express'); const WebSocketServer = require('ws').Server; const parseCookie = express.cookieParser('some secret'); / / load the parsing cookie middleware, set the password const MemoryStore = express. The session. The MemoryStore; // Load the session store to use const store = new MemoryStore(); const app = express(); const server = app.listen(process.env.PORT || 3000); app.use(parseCookie); app.use(express.session({ store: store, secret: 'some secret' })); App.use (express.static(__dirname + '/public')); App.get ('/random', function (req, res) {req.session.random = math.random ().toString(); res.send(200); }); // Set up the WebSocket server, Const webSocketServer = new webSocketServer ({server: server}); WebSocket webSocketServer.on('connection', function (ws) {let session; ws.on('message', function (data, flags) { const message = JSON.parse(data); // JSON sent by the client, Some code is needed to parse the JSON string to see if it is usable if (message.type === 'getSession') {parseCookie(ws. UpgradeReq, null, Function (err) {// Get the WebSocket session ID from the HTTP update request // Once the WebSockets server has a connection, The session ID = from initialization can be used in the request cookies for const sid = ws. UpgradeReq. SignedCookies [' connect. Sid ']; // The session can then use the session stored get method to load store.get(sid, function (err, loadedSession) { if (err) console.error(err); session = loadedSession; ws.send('session.random: ' + session.random, { mask: false, }); // a message containing the session value is sent back to the client}); }); } else { ws.send('Unknown command'); }}); }); <! DOCTYPE html> <html> <head> <script> const host = window.document.location.host.replace(/:.*/, ''); const ws = new WebSocket('ws://' + host + ':3000'); setInterval(function () { ws.send('{ "type": "getSession" }'); // Periodically send messages to the server}, 1000); ws.onmessage = function (event) { document.getElementById('message').innerHTML = event.data; }; </script> </head> <body> <h1>WebSocket sessions</h1> <div id='message'></div> <br> </body> </html>Copy the code

Express4 middleware

package describe
body-parser Parse the BODY data for the URL encoding and JSON POST request
compression Compressed server response
connect-timeout Request permission timeout
cookie-parser Cookies are parsed from the HTTP header information and the result is placed in req.cookies
cookie-session Use cookies to support simple sessions
csurf Add a token to a session to defend against CSRF attacks
errorhandler Default error handling used in Connect
express-session Simple session handling, using stores extensions to write session information to a database or file
method-override Map the new HTTP verb to _method in the request variable
morgan Log formatting
response-time Trace response time
serve-favicon Send site ICONS
serve-index Directory listing
whost Allow routes to match subdomain names

JWT

JSON Web Token (JWT) is currently the most popular cross-domain authentication solution.

Cross-domain authentication

The general process

  • The user sends the user name and password to the server
  • After the authentication, the server saves relevant data in the current session, such as user role, login time, and so on
  • The server returns a session_ID to the user and writes the user’s Cookie
  • The session_id is passed back to the server via cookies with each subsequent request from the user
  • The server receives the session_id, finds the previously saved data, and learns the user’s identity

Sharing session

In a server cluster, session data is required to be shared, and each server can read sessions:

  • One solution is to persist session data to a database or other persistence layer. Upon receiving the request, the various services request data from the persistence layer. The advantage of this scheme is the clear structure, but the disadvantage is the large amount of engineering. In addition, if the persistence layer fails, it will fail in a single point.
  • Another option is that the server does not store session data at all; all data is stored on the client side and every request is sent back to the server. JWT is an example of such a scheme.

JWT

The principle of

  • After authentication, the server generates a JSON object and sends it back to the user
  • When the user communicates with the server, the server sends back this JSON object, and the server identifies the user solely by this object
  • Tamper-proof will add a signature

The data structure

It’s the Payload. It’s the Signature.

  • Header: JSON, converted to a string using Base64 URL
  • Payload: JSON. The URL is converted into a string using Base64
  • Signature: The Signature of the first two parts

Header

{" alg ":" HS256 ", / / signature algorithm "typ" : "JWT / / the type of token}"Copy the code

Payload

{/ / seven official field "iss" : "issued by people", "exp" : "expiration date", "sub" : "theme" and "aud" : "audience", "diagindex.nbf" : "effective time", "iat" : "issued by time", "jit" : // Define private field "name": "Chenng"}Copy the code

Signature

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) # secret The secret key is known only by the serverCopy the code

use

  • 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 -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

koa

Core object

  • HTTP receives the parse response
  • Middleware execution context
  • All processes in Koa are middleware

The source code of

  • application
  • context
  • request
  • response

Use of middleware

const Koa = require('koa'); const app = new Koa(); const mid1 = async (ctx, next) => { ctx.body = 'Hi'; await next(); // Next executes the next middleware ctx.body += 'there'; }; const mid2 = async (ctx, next) => { ctx.type = 'text/html; chartset=utf-8'; await next(); }; const mid3 = async (ctx, next) => { ctx.body += ' chenng'; await next(); }; app.use(mid1); app.use(mid2); app.use(mid3); app.listen(2333); // Hi chenng thereCopy the code

Back to Media Resources

router .get('/api/dynamic_image/codewars', async (ctx, next) => { const res = await axios.get('https://www.codewars.com/users/ringcrl'); const [, kyu, score] = res.data .match(/<div class="stat"><b>Rank:<\/b>(.+?) <\/div><div class="stat"><b>Honor:<\/b>(.+?) <\/div>/); const svg = ` <svg xmlns="http://www.w3.org/2000/svg" width="80" height="20"> <rect x="0" y="0" width="80" height="20" fill="#fff" stroke-width="2" stroke="#cccccc"></rect> <rect x="0" y="0" width="50" height="20" fill="#5b5b5b"></rect> <text x="5" y="15" class="small" fill="#fff" style="font-size: 14px;" >${kyu}</text> <rect x="50" y="0" width="30" height="20" fill="#3275b0"></rect> <text x="53" y="15" class="small" fill="#fff" style="font-size: 14px">${score}</text> </svg> `; ctx.set('Content-Type', 'image/svg+xml'); ctx.body = Buffer.from(svg); await next(); });Copy the code

The Web API design

demand

  • Easy to use
  • Easy to modify
  • Good robustness
  • Not afraid to go public

Important criteria

  • Design easy to remember, functions at a glance
  • Use the appropriate HTTP methods
  • Choose the right English words. Pay attention to the singular and plural forms of the words
  • Use OAuth 2.0 for authentication

Take a look at some of the public Web API documentation at ProgrammableWeb (www.programmableweb.com)

Public key encryption Private key decryption

Generate public and private keys

Run the following command to generate a public key using OpenSSL: openssl genrsa -out rsa_private_key.pem 1024 Generate a private key: openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pemCopy the code

Crypto use

const crypto = require('crypto'); const fs = require('fs'); const publicKey = fs.readFileSync(`${__dirname}/rsa_public_key.pem`).toString('ascii'); const privateKey = fs.readFileSync(`${__dirname}/rsa_private_key.pem`).toString('ascii'); console.log(publicKey); console.log(privateKey); const data = 'Chenng'; console.log('content: ', data); // publicKey encryption const encodeData = crypto.publicencrypt (publicKey, buffer.from (data),).tostring ('base64'); // publicKey encryption const encodeData = crypto.publicencrypt (publicKey, buffer.from (data),).tostring ('base64'); console.log('encode: ', encodeData); Const decodeData = crypto. PrivateDecrypt (privateKey, buffer. from(encodeData, 'base64'),); console.log('decode: ', decodeData.toString());Copy the code

Redis cache interface

  • Redis is used to cache some data that does not need to be updated in real time
  • Use node-schedule to call interface redis on a nightly basis

    const redis = require(‘redis’); const redisClient = redis.createClient(); const getAsync = promisify(redisClient.get).bind(redisClient);

    let codewarsRes = JSON.parse(await getAsync(‘codewarsRes’)); if (! CodewarsRes) {const res = await axios. Get (‘ www.codewars.com/users/ringc… ‘); codewarsRes = res.data; redisClient.set(‘codewarsRes’, JSON.stringify(codewarsRes), ‘EX’, 86000); }

Node – the schedule is used

const schedule = require('node-schedule');
const axios = require('axios');

schedule.scheduleJob('* 23 59 * *', function () {
    axios.get('https://static.chenng.cn/api/dynamic_image/leetcode_problems');
    axios.get('https://static.chenng.cn/api/dynamic_image/leetcode');
    axios.get('https://static.chenng.cn/api/dynamic_image/codewars');
});
Copy the code

Reference Address:

  • https://juejin.cn/post/6844903775937757192?utm_source=gold_browser_extension
  • https://github.com/goldbergyoni/nodebestpractices
  • Node.js Hardball: 115 Core Tips