Review the content
In the last section, we talked about the Cookie+Session based authentication scheme.
Due to some disadvantages of the session-based scheme, the token based stateless Session management scheme was born. Stateless means that the server no longer stores information.
Jwt-based simple authentication process
Recommended reading for those unfamiliar with JWT:
- Server Authentication Artifact — JWT(1)
- JSON Web Token Introduction tutorial
Node implements JWT authentication
Technical implementation solution: Node + KOA2 + mongodb
The directory structure
Pre-development preparation
- node
- mongodb
Remember to start mongodb locally before starting node services
user.js
const mongoose = require("mongoose");
const { Schema } = mongoose;
const userSchema = new Schema({
name: String.password: String.salt: String.isAdmin: Boolean.age: Number
});
module.exports = mongoose.model("User", userSchema);
Copy the code
config.js
module.exports = {
'secret': 'ilovescotchyscotch'./ / key
'db': 'mongodb://localhost:27017/test'
}
Copy the code
package.json
{
"name": "token"."version": "1.0.0"."description": ""."main": "index.js"."dependencies": {
"crypto-js": "^" 3.1.9-1."jsonwebtoken": "^ 8.5.1"."koa": "^ 2.8.2"."koa-bodyparser": "^ 2"."koa-router": "^ 7.4.0"."mongoose": "^ 5.7.3." "
},
"devDependencies": {
"nodemon": "^ 1.19.3"
},
"scripts": {
"start": "nodemon ./app.js"
},
"keywords": []."author": ""."license": "ISC"
}
Copy the code
app.js
const Koa = require("koa");
const Router = require("koa-router");
const bodyParser = require("koa-bodyparser");
const md5 = require("crypto-js/md5");
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
const User = require("./models/user.js");
const config = require("./config.js");
const app = new Koa();
const router = new Router();
mongoose.connect(config.db, { useUnifiedTopology: true });
app.use(bodyParser());
/** * @description Create user */
router.post("/user".async (ctx, next) => {
const { username = "", password = "", age, isAdmin } = ctx.request.body || {};
if (username === "" || password === "") {
ctx.status = 401;
return (ctx.body = {
success: false.code: 10000.msg: "Username or password cannot be empty."
});
}
// Md5 the password first
const md5PassWord = md5(String(password)).toString();
// Generate a random salt
const salt = String(Math.random()).substring(2.10);
// Add salt and then md5
const saltMD5PassWord = md5(`${md5PassWord}:${salt}`).toString();
try {
// Similar to user search, save operations generally we will be encapsulated in an entity, this demo is only demonstration, do not write such a production environment
const searchUser = await User.findOne({ name: username });
if(! searchUser) {const user = new User({
name: username,
password: saltMD5PassWord,
salt,
isAdmin,
age
});
const result = await user.save();
ctx.body = {
success: true.msg: "Create successful"
};
} else {
ctx.body = {
success: false.msg: "Existing user with the same name"}; }}catch (error) {
// In this case, we usually throw the exception class directly, and then have the global error handling to handle
ctx.body = {
success: false.msg: "serve is mistakes"}; }});/** * @description User login */
router.post("/login".async (ctx, next) => {
const { username = "", password = "" } = ctx.request.body || {};
if (username === "" || password === "") {
ctx.status = 401;
return (ctx.body = {
success: false.code: 10000.msg: "Username or password cannot be empty."
});
}
// Generally, the client needs md5 encryption to transmit the password. Here I will encrypt it myself, assuming that the client does not encrypt it.
// Similar to user search, save operations generally we will be encapsulated in an entity, this demo is only demonstration, do not write such a production environment
try {
// Username is not allowed to duplicate during registration
const searchUser = await User.findOne({ name: username });
if(! searchUser) { ctx.body = {success: false.msg: "User does not exist"
};
} else {
// Need to go to the database to verify the user password
const md5PassWord = md5(String(password)).toString();
const saltMD5PassWord = md5(
`${md5PassWord}:${searchUser.salt}`
).toString();
if (saltMD5PassWord === searchUser.password) {
// Payload: indicates the Payload. It is not recommended to store sensitive information
const payload = {
id: searchUser._id
};
const token = jwt.sign(payload, config.secret, {
expiresIn: "2h"
});
ctx.body = {
success: true.data: {
token
}
};
} else {
ctx.body = {
success: false.msg: "Password error"}; }}}catch (error) {
ctx.body = {
success: false.msg: "serve is mistakes"}; }});/** * @description Obtain user information */
router.get(
"/user".async (ctx, next) => {
// There should be an Auth middleware
const token = ctx.request.query.token || ctx.request.headers["token"];
if (token) {
jwt.verify(token, config.secret, async function(err, decoded) {
if (err) {
return (ctx.body = {
success: false.msg: "Failed to authenticate token."
});
} else {
ctx.decoded = decoded;
awaitnext(); }}); }else {
ctx.status = 401;
ctx.body = {
success: false.msg: "need token"}; }},async (ctx, next) => {
try {
const { id } = ctx.decoded;
const { name, age, isAdmin } = await User.findOne({ _id: id });
ctx.body = {
success: true.data: { name, age, isAdmin }
};
} catch (error) {
ctx.body = {
success: false.msg: "server is mistakes"}; }}); app.use(router.routes()).use(router.allowedMethods()); app.on("error", (err, ctx) => {
console.error("server error", err, ctx);
});
app.listen(3000, () = > {console.log("Server listening on port 3000");
});
Copy the code
Test all interfaces using Postman.
- Verify the creation of the user interface
- Go to database validation
- Validate the business API
What problems are solved based on the JWT authentication scheme
- The server does not need to store authentication information. The authentication information is encrypted to the token. The server only needs to read the user information contained in the token.
- Avoids the problem that the shared Session is not easy to extend
- Do not rely on cookies, effectively avoid CORS attacks caused by cookies
- CORS can effectively solve cross-domain problems
Understanding of JWT and Token
Through this article about JWT discussing with Token, I have corrected some of my erroneous views. The next article is to record learning about token.
note
There are errors in welcome to be corrected, source address.
Finally interested to follow a wave of public number.