JWT
JSON Web Token( JWT)是目前最流行的跨域认证解决方案。
一般的跨域认证方式
流程如下:
- 客户端向服务器发送用户名和密码
- 服务器验证通过后,在session中保存相关数据
- 服务器向客户端返回一个session_token放到cookie中
- 客户端每次请求都通过cookie携带session_token
- 服务器拿到session_token,通过这个找到以前存的用户信息,进行判断
上面session中存放的数据,通过数据库或者别的持久化操作存储,但是会存在服务器集群数据同步的问题,处理起来比较麻烦。
JWT原理
服务器拿到用户登录信息,验证通过后会根据用户信息去生成一个Token并携带Signature返回给用户,服务器不存储session的信息,交由用户去存储。
JWT组成
xxxxx.yyyyy.zzzzz
对应的依次如下
- Header
- Payload
- Signature
中间通过.
分隔:Header.Payload.Signature
Header
使用的是Base64URL 算法转成字符串,解析后是个JSON对象,描述JWT的元数据
{
"alg": "HS256",
"typ": "JWT"
}
Payload
使用的是Base64URL 算法转成字符串,解析后是个JSON对象,存放JWT需要传输的信息
- iss 签发人
- exp 过期时间
- sub 主体信息
- iat 签发时间
- aud 受众
- nbf 生效时间
- jti 编号
注意,JWT 默认是不加密的,任何人都可以读到,所以不要把重要信息放在这个部分。
Signature
使用你需要的加密方式(默认HMAC SHA256加密)
HMACSHA256(base64UrlEncode(Header) + "." +base64UrlEncode(Payload),secret)
JWT客户端发送
客户端收到服务器返回的Token,存储在cookie、sessionStorage或者localStorage中
在需要认证的时候,将Token添加到Http请求的Header中
Header: {
Authorization: `Bearer ${Token}`
}
注意, Bearer和Token之间有个空格
在node中使用jwt
node-jsonwebtoken
jsonwebtoken是JWT的node版本 github
- 安装
yarn add jsonwebtoken
- 生成Token
const jwt = require('jsonwebtoken')
const token = jwt.sign({
// 设置一个小时的过期时间
exp: Math.floor(Date.now() / 1000) + 3600,
// 自定义的携带信息
data: {
name: user.name,
createTime: user.createTime
}
}, 'demo')
- JWT验证
jwt.verify(token, 'demo', (err, decode) => {
// decode是携带的信息
if (err) {
// 认证失败
// ...
} else {
// 成功
// ...
}
})
- 路由权限验证
由于本人使用的是koa搭建的服务端,所以下面是基于koa的操作
const koa = require('koa')
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser')
const jwtKoa = require('koa-jwt')
const app = new koa()
const router = new Router()
app.use(bodyParser())
// JWT拦截
/**
* secret是服务端存储的密钥
* unless是不做JWT验证的条件
* unless.method 不做验证的请求方式 这里是所有的GET请求不去做拦截
* unless.path 不做验证的请求路径 这里是/login请求不去做拦截
*
*/
// 特殊路径处理,这里的login用的是post
const specialPath = new RegExp(`^\/login`)
app.use(jwtKoa({ secret: 'demo' }).unless({ method: ['GET'], path: [specialPath] }))
router.get('/demo',(ctx, next) => {
})
router.post('/demo', (ctx, next) => {
})
router.del('/demo/:id', (ctx, next) => {
})
router.put('/demo/:id',(ctx, next) => {
})
app.use(router.routes()).use(router.allowedMethods())
jsonwebtoken 文档
jwt.sign(payload, secretOrPrivateKey, [options, callback])
异步:如果提供回调,则使用err或JWT 调用回调。
同步:将JsonWebToken返回为字符串。
payload
payload 必须是一个object, buffer或者string。注意, exp只有当payload是object字面量时才可以设置。
如果payload不是buffer或string,它将被强制转换为使用的字符串JSON.stringify()。
secretOrPrivateKey
secretOrPrivateKey 是包含HMAC算法的密钥或RSA和ECDSA的PEM编码私钥的string或buffer。
options
options:
- algorithm:加密算法(默认值:HS256)
- expiresIn:过期时间, 以秒表示或描述时间跨度zeit / ms的字符串。如60,"2 days","10h","7d"
- notBefore:生效时间, 以秒表示或描述时间跨度zeit / ms的字符串。如:60,"2days","10h","7d"
- audience:受众
- issuer:签发人
- jwtid:编号
- subject:主体
- noTimestamp
- header
在expiresIn,notBefore,audience,subject,issuer没有默认值时。也可以直接在payload中用exp,nbf,aud,sub和iss分别表示,但是你不能在这两个地方同时设置。
生成的jwts通常会默认包含一个iat值除非指定了noTimestamp。
jwt.sign({
exp: Math.floor(Date.now() / 1000) + (60 * 60),
data: '一个小时过期'
}, 'demo')
// 或者
jwt.sign({
data: '一个小时过期'
}, 'demo', { expiresIn: '1h' });
jwt.verify(token,secretOrPublicKey,[options,callback])
用法和jwt.sign差不多,用来验证token的合法性
jwt.decode(token [,options])
返回解码没有验证签名是否有效的payload, 用来获取payload
参考链接:JSON Web Token 入门教程,
jsonwebtoken文档, jwt.io,github: node-jsonwebtoken