preface
Recently, the company needs to build a set of Enterprise Mobility Management (EMM) Management platform. The requirements for such enterprise-oriented application Management itself are very complex. The construction of technical Management end and service end is the core of the architecture, and the client itself does not need to be so complex in the initial stage. As the head of the mobile terminal (in fact, a group leader of handyman), I am inevitably involved in the platform architecture. As a front-end Jser, I always receive such jobs that are not front-end like in the company. If I had been somewhat opposed to this kind of business, I need to consider a lot. Technology implementation itself is not easy to accumulate technology growth. This year I grew up too much, always trying to do some things that I may not like but are still meaningful, so I want to take over this task or do it well, so I want to consider participating in the EMM server construction. In fact, as long as anything wants to do it well, how can there be meaningful or meaningless difference?
Considering that services built based on Node.js are more and more popular at present, it is convenient to build micro-services on platform container cloud in the future. In addition, as a front-end Jser programmer, I am very familiar with using Node.js to build services. Having studied egg.js for some time before, I chose to build it based on the framework of egg.js without hesitation.
Why egg.js?
Last year, in gitChat’s advanced JavaScript Vue. Js + Node.js primer development, Amway came across egg.js for the first time, but was still amazed by it. Inherited from Koa, Egg is “convention over configuration”. According to a set of unified convention for application development, plug-in mechanism is relatively perfect. Although it is said that Egg inherits from Koa, people may think that they can implement a set of framework based on Koa and there is no need to build one based on this framework, but in fact, to design a set of such framework by themselves ultimately needs to learn from the strengths of each other, and it is not worthwhile in the short term in terms of time cost. Koa is a small but elegant framework, and Egg is designed for enterprise frameworks and applications, as the documentation says, so it is very convenient for us to quickly build a complete enterprise application. Egg features are already fairly well developed, and if you don’t have implemented features, it’s not hard to package them yourself with plug-ins provided by the Koa community.
ORM design and selection
In terms of database selection, MySQL is considered to be used in this project, instead of MongoDB. Egg-mysql plug-in is used at the beginning. After writing part of it, I find that too many things are written in service, and modification of table fields will affect too many codes. I read that you can introduce ORM frameworks such as Sequelize, and Egg officially provides egg-Sequelize.
What is ORM?
First, what is ORM?
Object Relational Mapping (ORM, OR O/RM, or O/R Mapping) is a programming technique used to transform data between different types of systems in object-oriented programming languages. In effect, it creates a “virtual object database” that can be used in a programming language.
Similar to the DAO design pattern in J2EE, the data objects in the program can be automatically transformed into corresponding tables and columns in the relational database, and references between data objects can also be transformed into tables through this tool. This solves the problem I had with table structure modification and data object manipulation being separate parts, making the code more maintainable. In fact, whether to choose ORM framework, and the previous front-end is to choose template engine or manual string, ORM framework avoids manual stitching SQL statements during development, can prevent SQL injection, in addition to the database and data CRUD decoupling, database replacement is relatively easier.
Sequelize framework
Sequelize is a popular ORM framework in the Node.js community.
- Sequelize.js documentation: docs.sequelizejs.com/
Sequelize use
Installation:
$ npm install --save sequelize
Copy the code
Establish a connection:
const Sequelize = require("sequelize");
// Complete usage
const sequelize = new Sequelize("database"."username"."password", {
host: "localhost".dialect: "mysql" | "sqlite" | "postgres" | "mssql".operatorsAliases: false.pool: {
max: 5.min: 0.acquire: 30000.idle: 10000
},
// SQLite only
storage: "path/to/database.sqlite"
});
//
const sequelize = new Sequelize("postgres://user:[email protected]:5432/dbname");
Copy the code
Verify connection is correct:
sequelize
.authenticate()
.then((a)= > {
console.log("Connection has been established successfully.");
})
.catch(err= > {
console.error("Unable to connect to the database:", err);
});
Copy the code
Define the Model:
Basic syntax for defining a Model:
sequelize.define("name", { attributes }, { options });
Copy the code
Such as:
const User = sequelize.define("user", {
username: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
}
});
Copy the code
For a Model field type design, there are several main considerations:
Sequelize adds createdAt and updatedAt by default, so it’s easy to know when data is created and updated. If you don’t want to use timestamps you can set attributes to timestamps: false;
Sequelize supports a wide variety of DataTypes, such as STRING, CHAR, TEXT, INTEGER, FLOAT, DOUBLE, BOOLEAN, DATE, UUID, JSON, and many others. See DataTypes for details.
Getters & Setters support is useful when we need to process fields, such as converting case to case.
const Employee = sequelize.define("employee", {
name: {
type: Sequelize.STRING,
allowNull: false,
get() {
const title = this.getDataValue("title");
return this.getDataValue("name") + "(" + title + ")"; }},title: {
type: Sequelize.STRING,
allowNull: false,
set(val) {
this.setDataValue("title", val.toUpperCase()); }}});Copy the code
The non-null check is determined by the allowNull attribute of the field. The type check is determined by the validate attribute. The underlying verification is implemented by validator.js. If a particular field of the model is set to allowNull (allowNull: true) and the value has been set to null, the validate property does not take effect. For example, there is a string field with allowNull set to true and validate that it is at least 5 characters long, but null is allowed.
const ValidateMe = sequelize.define("foo", {
foo: {
type: Sequelize.STRING,
validate: {
is: ["^[a-z]+$"."i"].// will only allow letters
is: /^[a-z]+$/i.// same as the previous example using real RegExp
not: ["[a-z]"."i"].// will not allow letters
isEmail: true.// checks for email format ([email protected])
isUrl: true.// checks for url format (http://foo.com)
isIP: true.// checks for IPv4 (129.89.23.1) or IPv6 format
isIPv4: true.// checks for IPv4 (129.89.23.1)
isIPv6: true.// checks for IPv6 format
isAlpha: true.// will only allow letters
isAlphanumeric: true.// will only allow alphanumeric characters, so "_abc" will fail
isNumeric: true.// will only allow numbers
isInt: true.// checks for valid integers
isFloat: true.// checks for valid floating point numbers
isDecimal: true.// checks for any numbers
isLowercase: true.// checks for lowercase
isUppercase: true.// checks for uppercase
notNull: true.// won't allow null
isNull: true.// only allows null
notEmpty: true.// don't allow empty strings
equals: "specific value".// only allow a specific value
contains: "foo".// force specific substrings
notIn: [["foo"."bar"]], // check the value is not one of these
isIn: [["foo"."bar"]], // check the value is one of these
notContains: "bar".// don't allow specific substrings
len: [2.10].// only allow values with length between 2 and 10
isUUID: 4.// only allow uuids
isDate: true.// only allow date strings
isAfter: "2011-11-05".// only allow date strings after a specific date
isBefore: "2011-11-05".// only allow date strings before a specific date
max: 23.// only allow values <= 23
min: 23.// only allow values >= 23
isCreditCard: true.// check for valid credit card numbers
// custom validations are also possible:
isEven(value) {
if (parseInt(value) % 2! =0) {
throw new Error("Only even values are allowed!");
// we also are in the model's context here, so this.otherField
// would get the value of otherField if it existed}}}}});Copy the code
Finally, we explain the design of the most important field primaryKey id, which needs to be specified by the field primaryKey: true. There are two main ways to design primary keys in MySQL: automatic increment; UUID.
AutoIncrement: True is the most convenient and efficient query method for small systems. However, this method is not conducive to distributed cluster deployment. This method has been used in MySQL applications, and will not be discussed in depth here.
UUID (also known as Globally Unique Identifiers) is a 128-bit unsigned integer that guarantees uniqueness across Space and Time. And no registration mechanism to guarantee, can be generated at any time on demand. According to WIKI, the probability of a randomly generated UUID repeating is 1 in 17 billion. The Sequelize data type contains uuids, UUID1, and UUID4. The node-uuid is based on RFC4122. Such as:
const User = sequelize.define("user", {
id: {
type: Sequelize.UUID,
primaryKey: true.allowNull: false.defaultValue: Sequelize.UUID1
}
});
Copy the code
The default id generates a UUID string, for example: ‘1C572360-faca-11e7-83Ee-9d836d45FF41’. Most of the time we don’t want this – character, we can do this by setting defaultValue, for example:
const uuidv1 = require("uuid/v1");
const User = sequelize.define("user", {
id: {
type: Sequelize.UUID,
primaryKey: true.allowNull: false.defaultValue: function() {
return uuidv1().replace(/-/g.""); }}});Copy the code
Using the Model object:
For Model object operations, Sequelize provides a series of methods:
- Find: Searches for a specific element in the database, either by findById or findOne;
- FindOrCreate: Searches for a particular element or creates it when it is not available;
- FindAndCountAll: Searches for multiple elements in the database and returns data and totals;
- FindAll: Search for multiple elements in the database;
- Complex filtering/OR/NOT queries;
- Manipulate datasets using limit, offset, order, and group;
- Count: Counts the number of occurrences of elements in the database;
- Max: Gets the maximum value of a particular attribute in a particular table;
- Min: Gets the minimum value for a particular attribute in a particular table;
- Sum: The sum of the values of a particular attribute;
- Create: Create database Model instance;
- Update: Update the database Model instance;
- Destroy: Destroys the database Model instance.
Data add, delete, modify and query (CRUD) can be implemented by the above methods, for example:
User.create({ username: "fnord".job: "omnomnom" })
.then((a)= >
User.findOrCreate({
where: { username: "fnord" },
defaults: { job: "something else" }
})
)
.spread((user, created) = > {
console.log(
user.get({
plain: true}));console.log(created);
/* In this example, findOrCreate returns an array like this: [ { username: 'fnord', job: 'omnomnom', id: 2, createdAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET), updatedAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET) }, false ] */
});
Copy the code
An egg – sequelize plug-in
Documentation: egg-sequelize: github.com/eggjs/egg-s…
Source analysis
Egg-sequelize /lib/loader.js
"use strict";
const path = require("path");
const Sequelize = require("sequelize");
const MODELS = Symbol("loadedModels");
const chalk = require("chalk");
Sequelize.prototype.log = function() {
if (this.options.logging === false) {
return;
}
const args = Array.prototype.slice.call(arguments);
const sql = args[0].replace(/Executed \(.+? \] : \ {0, 1} / s."");
this.options.logging.info("[model]", chalk.magenta(sql), ` (${args[1]}ms)`);
};
module.exports = app= > {
const defaultConfig = {
logging: app.logger,
host: "localhost".port: 3306.username: "root".benchmark: true.define: {
freezeTableName: false.underscored: true}};const config = Object.assign(defaultConfig, app.config.sequelize);
app.Sequelize = Sequelize;
const sequelize = new Sequelize(
config.database,
config.username,
config.password,
config
);
// app.sequelize
Object.defineProperty(app, "model", {
value: sequelize,
writable: false.configurable: false
});
loadModel(app);
app.beforeStart(function* () {
yield app.model.authenticate();
});
};
function loadModel(app) {
const modelDir = path.join(app.baseDir, "app/model");
app.loader.loadToApp(modelDir, MODELS, {
inject: app,
caseStyle: "upper".ignore: "index.js"
});
for (const name of Object.keys(app[MODELS])) {
const klass = app[MODELS][name];
// only this Sequelize Model class
if ("sequelize" in klass) {
app.model[name] = klass;
if (
"classMethods" in klass.options ||
"instanceMethods" in klass.options
) {
app.logger
.error(`${name} model has classMethods/instanceMethods, but it was removed supports in Sequelize V4.\
see: http://docs.sequelizejs.com/manual/tutorial/models-definition.html#expansion-of-models`); }}}for (const name of Object.keys(app[MODELS])) {
const klass = app[MODELS][name];
if ("associate" inklass) { klass.associate(); }}}Copy the code
The Sequelize object was instantiated when the plugin was initialized, and the Sequelize object was mounted to the App object. The Sequelize instantiation can also be accessed via app.model, where the Model object files are stored.
User Model design
Let’s use egg-sequelize as an example.
Installation:
$ npm i --save egg-sequelize
$ npm install --save mysql2 # For both mysql and mariadb dialects
Copy the code
Configuration:
App/config/plugin. Js configuration:
exports.sequelize = {
enable: true.package: "egg-sequelize"
};
Copy the code
App/config/config. Default. Js configuration:
// Configure database information
exports.sequelize = {
// Database type
dialect: "mysql".// host
host: "localhost"./ / the port number
port: "3306"./ / user name
username: "root"./ / password
password: "xxx".// Database name
database: "AEMM"
};
Copy the code
Model layer:
Using Sequelize directly is fine, but there are some problems. In team development, some people like to add their own timestamp, others like to add primary key, and custom table name. A large Web App usually has dozens of mapping tables, and one mapping table is a Model. Business code is not easy to write if you follow your preferences. The Model was not unified, and much of the code was not reusable. So we need a unified Model that forces all models to follow the same specification so that it is not only easy to implement, but also easy to unify the style.
The first thing we need to define is that the Model folder must be within the Models and be named after the Model, such as pet.js, user.js, etc. Second, each Model must follow a set of specifications:
- Unified primary key, the name must be ID, the type must be UUID;
- All fields default to NULL unless explicitly specified;
- Unified timestamp mechanism, each Model must have createdAt, updatedAt and Version, which record creation time, modification time and version number respectively.
So instead of using Sequelize’s API directly, we define the Model indirectly through db.js. For example, user.js should be defined as follows:
App/db. Js:
const uuidv1 = require("uuid/v1");
function generateUUID() {
return uuidv1().replace(/-/g."");
}
function defineModel(app, name, attributes) {
const { UUID } = app.Sequelize;
let attrs = {};
for (let key in attributes) {
let value = attributes[key];
if (typeof value === "object" && value["type"]) {
value.allowNull = value.allowNull && true;
attrs[key] = value;
} else {
attrs[key] = {
type: value,
allowNull: true
};
}
}
attrs.id = {
type: UUID,
primaryKey: true.defaultValue: (a)= > {
returngenerateUUID(); }};return app.model.define(name, attrs, {
createdAt: "createdAt".updatedAt: "updatedAt".version: true.freezeTableName: true
});
}
module.exports = { defineModel };
Copy the code
We define defineModel to enforce the above rules.
App/model/User. Js:
const db = require(".. /db");
module.exports = app= > {
const { STRING, INTEGER, DATE, BOOLEAN } = app.Sequelize;
const User = db.defineModel(app, "users", {
username: { type: STRING, unique: true.allowNull: false }, / / user name
email: { type: STRING, unique: true.allowNull: false }, / / email
password: { type: STRING, allowNull: false }, / / password
name: STRING, / / name
sex: INTEGER, // User gender: 1 male, 2 female, 0 unknown
age: INTEGER, / / age
avatar: STRING, / / avatar
company: STRING, / / the company
department: STRING, / / department
telePhone: STRING, // Contact number
mobilePhone: STRING, // Mobile phone number
info: STRING, // Remarks
roleId: STRING, / / character id
status: STRING, // User status
token: STRING, / / authentication token
lastSignInAt: DATE // Last login time
});
return User;
};
Copy the code
In database operation design, we usually script the table structure in advance, if you manually write the SQL to create the table, each time changing the table structure is actually a hassle. Sequelize provides Migrations to help create or migrate databases, and egg-Sequelize also provides a handy method. In the development phase, this can be done automatically using the following methods:
// {app_root}/app.js
module.exports = app= > {
if (app.config.env === "local") {
app.beforeStart(function* () {
yield app.model.sync({ force: true}); }); }};Copy the code
You can also add the following script to package.json:
The command | instructions |
---|---|
npm run migrate:new | Create a migration file to in./migrations/ |
npm run migrate:up | Perform the migration |
npm run migrate:down | Roll back a migration |
Package. Json:
. "scripts": { "migrate:new": "egg-sequelize migration:create --name init", "migrate:up": "egg-sequelize db:migrate", "migrate:down": "egg-sequelize db:migrate:undo" } ...Copy the code
Run Migrate :new NPM run Migrate :new
module.exports = {
async up(queryInterface, Sequelize) {
const { UUID, STRING, INTEGER, DATE, BOOLEAN } = Sequelize;
await queryInterface.createTable("users", {
id: {
type: UUID,
primaryKey: true.allowNull: false
}, // User ID (primary key)
username: {
type: STRING,
unique: true.allowNull: false
}, / / user name
email: {
type: STRING,
unique: true.allowNull: false
}, / / email
password: {
type: STRING,
allowNull: false
}, // Login password
name: STRING, / / name
age: INTEGER, // User age
info: STRING, // Remarks
sex: INTEGER, // User gender: 1 male, 2 female, 0 unknown
telePhone: STRING, // Contact number
mobilePhone: STRING, // Mobile phone number
roleId: STRING, / / character ID
location: STRING, / / local
avatar: STRING, / / avatar
company: STRING, / / the company
department: STRING, / / department
emailVerified: BOOLEAN, // Mailbox authentication
token: STRING, // Identity token
status: { type: INTEGER, allowNull: false }, // User status: 1 enabled, 0 disabled, 2 hidden, 3 deleted
createdAt: DATE, // User creation time
updatedAt: DATE, // Update time of user information
lastSignInAt: DATE // Last login time
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable("users"); }};Copy the code
User authentication selection
The so-called user Authentication (Authentication), is to allow users to log in, and in the following period of time to allow users to access the website to use their account, without the need to log in again mechanism.
Tip: Don’t confuse user authentication with Authorization. User authorization refers to specifying and allowing users to use their own rights, such as Posting, managing a site, etc.
User authentication is divided into two parts:
- A user generates and obtains a Token by logging in with a user name and password.
- The user uses the Token to authenticate the user to obtain related information.
JSON Web Token (JWT) specification
JSON Web Token (JWT) is a very lightweight specification. This specification allows us to use JWT to deliver secure and reliable information between the user and the server.
The composition of JWT
A JWT is essentially a string made up of three parts, a header, a payload, and a signature.
Header
JWT requires a header that describes the most basic information about the JWT, such as its type and the algorithm used to sign it. This can also be represented as a JSON object.
{
"typ": "JWT"."alg": "HS256"
}
Copy the code
Here, we show that this is a JWT and that the signature algorithm we use is the HS256 algorithm. It is Base64 encoded as well, and then the string becomes the JWT Header.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
Copy the code
Here we use the base64URL module for Base64 encoding to get this string. The test code is as follows:
const base64url = require("base64url");
let header = {
typ: "JWT".alg: "HS256"
};
console.log("header: " + base64url(JSON.stringify(header)));
// header: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
Copy the code
Fact: Base64 is a code, which means it can be translated back to its original form. It is not an encryption process.
Payload
To put it bluntly, it is the data we need to include, similar to the request body of the network request, for example:
{
"iss": "zhaomenghaun"."sub": "*@agree.com.cn"."aud": "www.agree.com.cn"."exp": 1526875179."iat": 1526871579."id": "49a9dd505c9d11e8b5e86b9776bb3c4f"
}
Copy the code
The first five fields are defined by JWT standards.
- Iss: The issuer of the JWT
- Sub: The user for which the JWT is intended
- Aud: The party receiving the JWT
- Exp (Expires): When does it expire, in this case a Unix timestamp
- Iat (issued at): When was it issued
Base64 encoding the following JSON object yields the following string, which we call the Payload of JWT.
const base64url = require("base64url");
let payload = {
id: "49a9dd505c9d11e8b5e86b9776bb3c4f".iat: 1526871579.exp: 1526875179
};
console.log("payload: " + base64url(JSON.stringify(payload)));
// payload: eyJpZCI6IjQ5YTlkZDUwNWM5ZDExZThiNWU4NmI5Nzc2YmIzYzRmIiwiaWF0IjoxNTI2ODcxNTc5LCJleHAiOjE1MjY4NzUxNzl9
Copy the code
Signature
Use periods for both of the above encoded strings. Joined together (head first), they form:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjQ5YTlkZDUwNWM5ZDExZThiNWU4NmI5Nzc2YmIzYzRmIiwiaWF0IjoxNTI2ODcxNTc5LCJleHA iOjE1MjY4NzUxNzl9Copy the code
Finally, we use HS256 algorithm to encrypt the above concatenated string. When encrypting, we also need to provide a secret. We can use Node-jWA for HS256 encryption. If we use 123456 as the key, then we can get our encrypted content, which is also called the signature. The final step in the signing process is actually signing the header and payload contents.
const jwa = require("jwa");
const hmac = jwa("HS256");
let secret = "123456";
const signature = hmac.sign(
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjQ5YTlkZDUwNWM5ZDExZThiNWU4NmI5Nzc2YmIzYzRmIiwiaWF0IjoxNTI2ODcxNTc5LCJleH AiOjE1MjY4NzUxNzl9",
secret
);
console.log("signature: " + signature);
// signature: JtrTx9QaN3BD1QkZhY58MTu6WHn_vQwRBxO9VwJgkhE
Copy the code
Finally, this part of the signature is also spliced at the end of the signed string, and we get the complete JWT as follows:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjQ5YTlkZDUwNWM5ZDExZThiNWU4NmI5Nzc2YmIzYzRmIiwiaWF0IjoxNTI2ODcxNTc5LCJleHA iOjE1MjY4NzUxNzl9.JtrTx9QaN3BD1QkZhY58MTu6WHn_vQwRBxO9VwJgkhECopy the code
After the whole process, we need to think about whether Token is safe and whether sensitive information can be transmitted.
We now understand that a token consists of the Base64 encoding of Header, the Base64 encoding of Payload, and the Signature. When someone else gets our token, You can decode the first two Base64 segments of the Token to obtain Header and Payload objects. Here, we can decode our Token directly by using the Node-JsonWebToken module decode method.
const jwt = require("jsonwebtoken");
let decoded = jwt.decode(
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjQ5YTlkZDUwNWM5ZDExZThiNWU4NmI5Nzc2YmIzYzRmIiwiaWF0IjoxNTI2ODcxNTc5LCJleH AiOjE1MjY4NzUxNzl9.JtrTx9QaN3BD1QkZhY58MTu6WHn_vQwRBxO9VwJgkhE",
{ complete: true});console.log("jsonwebtoken: " + JSON.stringify(decoded));
// jsonwebtoken: {"header":{"typ":"JWT","alg":"HS256"},"payload":{"id":"49a9dd505c9d11e8b5e86b9776bb3c4f","iat":1526871579,"exp":15268751 79},"signature":"JtrTx9QaN3BD1QkZhY58MTu6WHn_vQwRBxO9VwJgkhE"}
Copy the code
Therefore, our payload cannot contain sensitive information such as passwords. For us, the ID here is a string of UUID, and even if we get it, we cannot directly determine the relevant content, so as not to directly reveal our content.
In general, encryption algorithms always produce different outputs for different inputs. For two different inputs, the probability of producing the same output is extremely small. If the contents of the header and payload are decoded and modified, then the signature of the new header and payload will be different from the original signature, and the resulting signature will certainly be different without knowing the key used by the server for encryption.
So when the server gets the JWT, it first checks whether the signature is expired and whether the JWT obtained by re-signing the header and payload contents using the same algorithm (specified by the ALG field in the JWT header) is consistent with the JWT passed by the user. If the server uses the same method to sign the header and payload again and finds that the signature calculated by the server is different from the received signature, then the Token has been modified. We should reject the Token and return an HTTP 401 Unauthorized response.
An egg – JWT plug-in
Documents: github.com/okoala/egg-…
Egg-jwt is implemented based on node-jsonWebToken. The complete documentation can be found at github.com/auth0/node-… . The JWT object is mounted under the app object and can be accessed through app.jwt in three methods:
- Jwt. sign(payload, secretOrPrivateKey, [options, callback])———— Generates a token string
- Jwt. verify(token, secretOrPublicKey, [options, callback])———— Verifies the token validity
- Jwt. decode(token [, options])———— Decode the token
Installation:
$ npm i egg-jwt --save
Copy the code
Configuration:
App/config/plugin. Js configuration:
exports.jwt = {
enable: true.package: "egg-jwt"
};
Copy the code
App/config/config. Default. Js configuration:
exports.jwt = {
enable: false.secret: "xxxxxxxxxxxxx"
};
Copy the code
Call:
Request header:
Authorization: Bearer {access_token}
Copy the code
Note: Access_token is the token value returned after login.
App/service/user. Js:
/** * Generate Token * @param {Object} data */
createToken(data) {
return app.jwt.sign(data, app.config.jwt.secret, {
expiresIn: "12h"
});
}
/** * Verify token validity * @param {String} token */
verifyToken(token) {
return new Promise((resolve, reject) = > {
app.jwt.verify(token, app.config.jwt.secret, function(err, decoded) {
let result = {};
if (err) {
/* err = { name: 'TokenExpiredError', message: 'jwt expired', expiredAt: 1408621000 } */
result.verify = false;
result.message = err.message;
} else {
result.verify = true;
result.message = decoded;
}
resolve(result);
});
});
}
Copy the code
The extend/helper. Js:
/ / access Token
exports.getAccessToken = ctx= > {
let bearerToken = ctx.request.header.authorization;
return bearerToken && bearerToken.replace("Bearer "."");
};
/ / validation Token
exports.verifyToken = async (ctx, userId) => {
let token = this.getAccessToken(ctx);
let verifyResult = await ctx.service.user.verifyToken(token);
if(! verifyResult.verify) { ctx.helper.error(ctx,401, verifyResult.message);
return false;
}
if(userId ! = verifyResult.message.id) { ctx.helper.error(ctx,401."User ID inconsistent with Token");
return false;
}
return true;
};
// The response was processed successfully
exports.success = (ctx, result = null, message = "Request successful", status = 200) = > {
ctx.body = {
code: 0.message: message,
data: result
};
ctx.status = status;
};
// Process failed response
exports.error = (ctx, code, message) = > {
ctx.body = {
code: code,
message: message
};
ctx.status = code;
};
Copy the code
Call in controller:
/ / Token is generated
let token = ctx.service.user.createToken({ id: user.id });
// Verify the Token validity
let isVerify = await ctx.helper.verifyToken(ctx, id);
if (isVerify) {
// Valid logic
// ...
}
Copy the code
In this way, restful apis that require identity authentication can be authenticated using tokens to implement user authentication and authorization.
Afterword.
In this paper, by the design of user management was originally wanted to in the construction of Node. Js service process and harvest, the problem of too long didn’t write the article, thinking slow to spread, only the most basic usage of used in the design process of plug-ins and some design thinking, to not able to help others, but beg to be able to help you comb clear train of thought, After writing, I found that my cognition is really clear, and many doubts before suddenly became clear.
I have not written many articles. In the past six months, I have been mainly responsible for the architecture design and module development of hybrid mobile terminal. I have been working on the JS SDK and the native base for nearly a year.
This half year saw many framework source code, and try to write some of the basic framework and internal documents and notes, but not in the open source community and sharing, look back, after all, some regret, although you could have been very busy no time to comfort myself, but looking back on it actually squeeze still has some time, so the follow-up will spare more time to archive, After all, writing it out really makes you understand it better.
reference
- JSON Web Token – Securely transfers information between Web applications
- Design a single sign-on system using JSON Web Tokens