什么是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
}
})
})
复制代码