什麼是Token?web
Token指訪問資源的憑據,是一種身份認證的方式,它是解決跨域認證的最流行的一種方式。算法
爲何用Token?數據庫
之前較爲流行的是經過session去作身份認證,session是經過服務器中保存會話數據來作身份認證,這種方式會致使在高併發中服務器壓力過大的狀況,還有就是,若是是服務器集羣,那麼就須要這些服務器session共享。express
Token不在服務器中保存會話數據,而是保存在客戶端。每次請求的headers中存入Token,在服務器中判斷Token的有效性,是否能夠訪問資源。npm
傳統Token和JWT的區別json
傳統Tokenapi
用戶發起登陸請求,登陸成功以後返回Token,而且存於數據庫,用戶訪問資源的時候須要攜帶Token,服務端獲取Token以後和數據庫中的對比。跨域
JWTbash
用戶發起登陸請求,登陸成功以後返回Token,可是不存於數據庫,用戶訪問資源的時候須要攜帶Token,服務端獲取Token以後去校驗Token的合法性。服務器
JWT分爲三個部分header、payload、verify signature
header
內部包含有簽名算法、Token類型,而後經過base64url算法轉成字符串
//明文例子:
{
"alg":"HS256",
"typ":"JWT"
}
複製代碼
payload
內部包含JWT標準數據和自定義數據,而後經過base64url算法轉成字符串
JWT標準數據常見的有:
可選擇性使用以上標準數據
//明文例子:
{
"id": 3,
"name": "Bmongo",
"age": 18,
"iat": 1588139323,
"exp": 1588139333
}
複製代碼
注意:因爲JWT是默認不加密的,因此在這邊不要存敏感信息
verify signature
這部分是對前兩部分的簽名,防止數據的篡改
secret是服務器端保存的密鑰,只有服務器端知道,再使用header中所指定的簽名算法對上面的倆部分進行簽名,按照如下公式生成簽名
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
複製代碼
算出簽名以後,把三部分經過.分隔開返回給用戶就好了
JWT例子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTAsIm5hbWUiOiLlvKDkuIkiLCJhZ2UiOjE2LCJpYXQiOjE1ODgxMzkzMjMsImV4cCI6MTU4ODEzOTMzM30.WzZp_aNgiw4iTsX7buxMhZe0z0e94Ve6ImEZ8L8L78c
複製代碼
客戶端請求
每次客戶端的請求都須要帶上這個token,通常是把token寫入到請求的headers中
Node.js中使用JWT
經過npm包jsonwebtoken來完成token的生成和驗證
npm install --save jsonwebtoken
複製代碼
const jwt = require("jsonwebtoken")
//撒鹽,加密時候混淆
const secret = '113Bmongojsdalkfnxcvmas'
//生成token
//info也就是payload是須要存入token的信息
function createToken(info) {
let token = jwt.sign(info, secret, {
//Token有效時間 單位s
expiresIn:60 * 60 * 10
})
return token
}
//驗證Token
function verifyToken(token) {
return new Promise((resolve, reject) => {
jwt.verify(token, secret, (error, result) => {
if(error){
reject(error)
} else {
resolve(result)
}
})
})
}
複製代碼
const express = require("express")
const app = express()
const jwt = require("jsonwebtoken")
//撒鹽,加密時候混淆
const secret = '113Bmongojsdalkfnxcvmas'
const user = {
id:10,
name:"Bmongo",
age:16,
}
//生成token
//info也就是payload是須要存入token的信息
function createToken(info) {
let token = jwt.sign(info, secret, {
//Token有效時間 單位s
expiresIn:60 * 60 * 10
})
return token
}
//驗證Token
function verifyToken(token) {
return new Promise((resolve, reject) => {
jwt.verify(token, secret, (error, result) => {
if(error){
reject(error)
} else {
resolve(result)
}
})
})
}
//設置容許跨域
app.use(function(req, res, next) {
//指定容許其餘域名訪問 *全部
res.setHeader("Access-Control-Allow-Origin", "*");
//容許客戶端請求頭中帶有的
res.setHeader("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
//容許請求的類型
res.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.setHeader("X-Powered-By",' 3.2.1')
//讓options請求快速返回
if(req.method=="OPTIONS") res.send(200);
else next();
});
//白名單
const whiteList = ['/login']
app.use((req,res,next) => {
if(!whiteList.includes(req.url)) {
verifyToken(req.headers.authorization).then(res => {
next()
}).catch(e => {
res.status(401).send('invalid token')
})
} else {
next()
}
})
app.post('/login',(req,res) => {
let token = createToken(user)
res.json({token})
})
app.get("/api/info", (req,res) => {
res.send({
result:1,
data:{
"name":"Bmongo",
"id":1
}
})
})
複製代碼