Session brief introduction
Session It is a very important and popular User authentication
And to grant authorization
The way .
authentication
: Let the server know who you are
to grant authorization
: Let the server know what you can and can't do
Session The advantages of
- comparison
JWT
, The biggest advantage is that you can take the initiative to removesession
了 ( becausesession
It is saved on the server , The server can actively clear ;JWT
In order toToken
The form is saved on the client , As long as it doesn't expire , The client can always holdToken
To authenticate and authorize users ) session
Save on the server side , Relatively safe- combination
cookie
Use , More flexible , Good compatibility
session The disadvantages of
cookie+session
stayCross domain
The scene didn't perform well (cookie Non cross domain )- If distributed deployment , Need to do
Multi machine sharing
session Mechanism - be based on
cookie
It's easy to beCSRF
(CSRF
yesCross-site request forgery
, An attack , It can use yourcookie
The attack ) - Inquire about
session
Information may have database query operations ( Want to get the completesession
Information needs to be taken session_id To query the database , Query requires time and computing power , This will cause some performance problems .)
Session Related concepts
- session: It is mainly stored in
Server side
, A relatively safe - cookie: It is mainly stored in
client
, And it's not very safe - sessionStorage: Valid only in current session , Cleared after closing page or browser
- localstorage: Unless removed , Otherwise permanent
JWT brief introduction
What is? JWT?
- JSON Web Token It's an open standard (RFC 7519)
- A compact and independent approach is defined , Information between the parties can be used as
JSON
Object for secure transfer - This information can be verified and trusted , Because it's digitally signed
JWT The composition of the
- Head (Header)
- Payload (Payload)
- Signature (Signature)
JWT It's divided into three parts , Each part is separated by black dots
Header
Header The essence is a JSON, This JSON There are 2 A field
- typ:token The type of , There are fixed JWT
- alg: The use of hash Algorithm , for example :HMAC SHA256 perhaps RSA
Header Before and after coding
- {“alg”:“HS256”,"typ":"JWT"}
- After coding is a paragraph Base64 character string
Payload
- Store the information that needs to be transmitted , Such as user ID、 User name, etc
- It also contains metadata , Such as expiration date 、 Publisher, etc
- And Header Different ,Playload Can encrypt
Playload Before and after coding
- {“user_id”:"xiaofengche"}
- After coding is a paragraph Base64 character string
Signature
- Yes Header and Payload Part to sign
- Guarantee Token It has not been tampered with or damaged during transmission
Signature Algorithm
Signature
= HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
After generating the signature, you still need to Base64 code
JWT working principle
client ( browser ) adopt POST request
Pass the user name and password to the server , The server checks the user name and password , After successful verification, the user will be ID And other information as JWT
Of Payload
, Compare it with the head base64 code
And then form a JWT
, Then the back end returns that string to the front end as the return result of the login success request , Then the front end saves it in localStorage
perhaps sessionStorage
in .
After that, every request from the front end will send JWT String as Http
Inside the head Authorization( authentication )
, And send it to the back end , The backend checks whether it exists , If present, verify JWT
Validity of string ( For example, whether the signature is correct , Whether the token has expired, etc ).
After the verification is passed , The back end uses JWT Perform other business logic on the user information contained in and return the corresponding results .
JWT vs. Session
- Extensibility
JWT Seamless access Horizontal expansion
, Because it is based on Token
( token ) Your authentication is No state
Of , So there's no need to session
Store user information in , Applications can be easily expanded , have access to Token Access resources from different servers , Don't worry about whether the user really logs in to a server .
- Security
Both will be attacked .
- RESTful API
RESTful
The required procedure is No state
Of , image session
This is a A stateful
Method of authentication , Obviously you can't do RESTful API Of .
- performance
When the client sends a request to the server , There may be a lot of user information in JWT
in , Every Http request
Will incur a lot of overhead ; If you use session If so, just a small amount of expenses , because session_id
A very small ,JWT May be several times its size .
however session_id
There are also shortcomings. , Searching for complete information requires session_id
, This is also a performance drain ;JWT The string contains complete information ,JWT You don't need a database query , Less performance consumption ,JWT Equivalent to exchanging time with space
- timeliness
JWT
The timeliness of is better than session
almost . because JWT
Only wait until the expiration time to destroy , Can't update in real time ,session
It can be actively and manually destroyed on the server .
stay Node.js Use in JWT
- install jsonwebtoken
npm i jsonwebtoken
- Signature
Enter at terminal node
Command line , introduce jwt
Use sign
Signature method , Its first parameter is JSON object , The second parameter can write the key
> token = jwt.sign({name:"xiaofengche"},'secret');
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGlhb2ZlbmdjaGUiLCJpYXQiOjE2Mjg0MDYzMzF9.zOCf0dzBRvuBjOCcZZ5nuLbUGd4q05SQuFod48ScML4'
Get token Then it can be passed to the client , The client can hold this every request token Put it on the head and send it back to the server , The server gets token Then you can judge who the current user is , What authority do you have .
- verification
Use decode decode
You can judge who the user is
> jwt.decode(token);
{ name: 'xiaofengche', iat: 1628406331 }
there iat
It refers to the time at the time of signature , The unit is seconds
need verification
Whether the user information has been tampered with ,verify
The second parameter is the key to be encrypted
> jwt.verify(token,'secret');
{ name: 'xiaofengche', iat: 1628406331 }
prove token It's legal. , The signature is also legal
Implement user registration
- Design users Schema
Need to redesign Schema, Add the password field .
const mongoose = require('mongoose'); //mongoose Provided Schema Class to generate documents Schema const { Schema,model } = mongoose
const userSchema = new Schema({
// Hide useless information
__v:{type:Number,select:false},
//required Indicates that this attribute is required
//default Default values can be set
name:{type:String,required:true},
// Sensitive information like passwords should not be exposed casually , It needs to be hidden ——select:false
password:{type:String,required:true,select:false},
});
// Build a model
//User: Name the document collection
module.exports = model('User',userSchema);
Add the definition of the new field in the relevant operation
// Create user async create(ctx){ // Verify the of the request body name Bit string type and is required ctx.verifyParams({ // Mandatory :required Delete also defaults to true name:{ type:'string',required:true }, password:{type:'string',required:true}, }); const user = await new User(ctx.request.body).save(); ctx.body = user; } // Update user async update(ctx){
ctx.verifyParams({ // Mandatory :required Delete also defaults to true name:{ type:'string',required:false }, password:{type:'string',required:false}, }); const user = await User.findByIdAndUpdate(ctx.params.id,ctx.request.body); if(!user){ctx.throw(404,' The user doesn't exist ');} ctx.body = user;
}
Due to the modification of user attributes, you can partially modify , Therefore, you need to modify the request method for changing the route
//put It's a whole replacement , Now users can update some properties
router.patch('/:id',update);
- Write logic to ensure uniqueness ( User uniqueness )
When creating a user, write logic to ensure uniqueness , Ensure that the user name does not duplicate when creating
// Update user async update(ctx){
ctx.verifyParams({ // Mandatory :required Delete also defaults to true name:{ type:'string',required:false }, password:{type:'string',required:false}, }); // Get the user name in the request body const {name} = ctx.request.body // findOne Return the first qualified user const repreatedUser = await User.findOne({name}); // If there are duplicate users, return 409 The status code represents a conflict if(repreatedUser){ ctx.throw(409," The user name is already in use "); } const user = await User.findByIdAndUpdate(ctx.params.id,ctx.request.body); if(!user){ctx.throw(404,' The user doesn't exist ');} ctx.body = user;
}
Log in and get token
- Login interface design
The action of login does not belong to any kind of user addition, deletion, modification and query , Can imitate github use POST+ Verb
form
- use jsonwebtoken Generate token
First, in the config.js Configure key
secret:'jwt-secret',
stay users.js introduce jsonwebtoken And the key , Then implement the login interface
const jsonwebtoken = require('jsonwebtoken'); const {secret} = require('../config');
// Sign in
async login(ctx){
ctx.verifyParams({
name:{type:'string',required:true},
password:{type:'string',required:true},
});
// There are two cases of login : The user name does not exist or the password is wrong , Login failed ; Login successful
// Find the first user who meets the criteria
const user = await User.findOne(ctx.request.body);
if(!user){ctx.throw(401,' Username or password incorrect ');}
// obtain id and name
const {_id,name} = user;
// Login successfully generated token, The parameters are user insensitive information , Signature key , Expiration time
//1d: One day
const token = jsonwebtoken.sign({_id,name},secret,{expiresIn:'1d'});
ctx.body = {token};
}
Finally, don't forget to be here routes->users.js Registered interface
//delete Is the key word , Take the alias
const {find,findById,create,update,delete:del,login} = require('../controllers/users');
router.post('/login',login)
Effect demonstration :
Write your own Koa Middleware realizes user authentication and authorization
- authentication : verification token, And get the user information
stay routes->users.js Write authentication middleware .
Suppose the client is through Authorization Field add Bearer Space +token This form puts token incoming , We know how to get token 了
const jsonwebtoken = require('jsonwebtoken');
const {secret} = require('../config');
const auth = async(ctx,next) => {
// When not set authorization Set it to an empty string
const {authorization = ''} = ctx.request.header;
// Get rid of 'Bearer ' That's what we really want token
const token = authorization.replace('Bearer ','');
// Verify user information
try{
const user = jsonwebtoken.verify(token,secret);
ctx.state.user = user;
}catch(err){
// All validation failures are manually thrown into 401 error , That is, not certified
ctx.throw(401,err.message);
}
await next();
}
Finally, put the middleware on the interface that needs authentication
router.patch('/:id', auth,update);
router.delete('/:id',auth,del);
- to grant authorization : Use middleware to protect the interface
stay users.js Write authentication Middleware in the controller ( It can also be in... Like above routes->users.js Inside )
async checkOwner(ctx,next){
// Judge the current modified or deleted user id Is it the name of the currently logged in user id
if(ctx.params.id !== ctx.status._id){
// The object of the operation does not throw an error itself
ctx.throw(403,' No authority ')
}
await next();
}
Finally, add the middleware to the interface that needs authentication
const {find,findById,create,update,delete:del,login,checkOwner} = require('../controllers/users');
router.patch('/:id', auth,checkOwner,update);
router.delete('/:id',auth,checkOwner,del);
use koa-jwt Middleware realizes user authentication and authorization
- install koa-jwt:
npm i koa-jwt --save
This is a third-party middleware , Powerful . With this middleware , We don't need to write our own middleware .
- Use middleware to protect the interface
Introduce middleware , Only one line of code can replace the authentication middleware written by yourself .
const jwt = require('koa-jwt');
const auth = jwt({ secret });
- Use middleware to obtain user information
koa-jwt
Also store user information in ctx.state.user
On , The custom authorization middleware can still be used normally .