JWT 全称为 JSON Web Token,是一份开源的标准协议,它定义了一种传输内容基于 JSON、轻量级、安全的数据传输方式。
每个 JWT 都由 Header、Payload、Signature 3 部分组成,同时用点进行拼接,形式如下:
Header.Payload.Signature
Header 部分是一个经过 Base64 编码后的 JSON 对象。对象的内容通常包括 2 个字段,形式如下:
{
"typ": "JWT",
"alg": "HS256"
}
其中,typ(全称为 type)指明当前的 Token 类型为 JWT,alg(全称为 algorithm)指明当前的签名算法是 HS256。
Payload 部分也是一个经过 Base64 编码后的 JSON 对象,对象的属性可以划分成 3 部分:保留字段、公共字段、私有字段。
保留字段是 JWT 内部声明,具有特殊作用的字段,包括
公共字段和私有字段都是用户可以任意添加的字段,区别在于公共字段是一些约定俗成,被普遍使用的字段,而私有字段更符合实际的应用场景。
当前已有的公共字段可以从 JSON Web Token Claims 中找到。
Payload 的结构形式如下:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Signature 部分是 JWT 根据已有的字段生成的,它的计算方式是使用 Header 中定义的算法,使用用户定义的密钥,对经过 Base64 编码后的 Header 和 Payload 组成的字符串进行加密,形式如下:
HMACSHA256(base64(header) + '.' + base64(payload))
业界普遍认可的应用场景主要有以下几种:
数据数据篡改指的是数据在传输过程中被截获,修改的行为。
JWT 本身可以使用加密算法对传输内容进行签名,即使数据被截获,也很难同时篡改签名和传输内容。
鉴权指的是验证用户是否有访问系统的权利。
部分人使用 JWT 来取代传统的 Session + Cookie,理由是:
基于 JWT 的鉴权一般处理逻辑是:
基于 JWT 的鉴权方案也存在一些争议:
以下使用 Node.js 和 JavaScript 演示 JWT 在鉴权方面的应用,涉及的库有:
Token 的生成一般是客户端发送登录请求,服务器使用密钥生成 Token 并放入响应体中,以下为服务端的 Token 生成逻辑。
// 文件位置:controller/v1/token.js
const config = require('config') // 加载服务器配置
const jwt = require('jsonwebtoken') // 加载 jwt Node.js 语言实现
/**
* 创建 Token 控制器
* @param {Object} ctx 请求上下文
*/
async function create(ctx) {
const username = ctx.request.body.username
const password = ctx.request.body.password
if (!username || !password) {
ctx.throw(400, '参数错误')
return
}
// 省略:用户名密码数据库校验
const user = { id: '5e54c02a2b073de564fe8034' } // 用户信息
const secret = config.get('secret') // 获取保存于配置中的密钥
const opt = { expiresIn: '2d' } // 设置 Token 过期时间为 2 天
ctx.body = jwt.sign(user, secret, opt) // 生成并返回 token
}
module.exports = {
create,
}
客户端一般情况下将 Token 放在 Http Header 的 Authorization 中,随请求发送给服务器。
// 文件位置:views/index.pug
var request = axios.create({ baseURL: '/api/v1' }) // 创建请求实例
var token // 为了方便这里使用全局变量,正常情况下应该放入其他存储介质中,如,localStorage,此处省略获取逻辑
// 监听正常请求按钮单击事件,发起请求
document.querySelector('#normal').addEventListener('click', function() {
if (!token) {
alert('请登录')
return
}
request.get('/users', {
headers: {
Authorization: 'Bearer ' + token, // 绑定 token 到 header 中
},
}).then(function({ data }) {
document.querySelector('#response').innerHTML = JSON.stringify(data)
}).catch(function(err) {
console.log('Request Error: ', err)
})
})
验证操作一般放在服务器的中间件。
const config = require('config') // 加载服务器配置
const jwt = require('jsonwebtoken') // 加载 jwt Node.js 语言实现
// 定义中间件函数
module.exports = async (ctx, next) => {
const path = ctx.url // 获取请求 URL
const method = ctx.method.toLowerCase() // 获取请求方法
// 请求白名单,白名单中的请求不经过中间件 token 校验
const whiteList = [
{ path: /^\/api\/v[1-9]\/tokens/, method: 'post' },
{ path: /^\/api/, reverse: true }, // 非 /api 开头的资源都不需要经过请求校验
]
// 请求白名单检查函数
const checker = (i) => {
const matchPath = i.path.test(path)
const matchMethod = i.method ? i.method === method : true
return (i.reverse ? !matchPath : matchPath) && matchMethod
}
// 白名单逻辑判断
if (whiteList.some(checker)) {
await next()
return
}
// 获取 http header 中的 token
const token = (ctx.header.authorization || '').replace('Bearer ', '')
// token 有效性校验
try {
const data = jwt.verify(token, config.secret)
ctx.userInfo = data
} catch (e) {
ctx.throw(400, 'Token 错误')
}
await next()
}
查看完整代码请前往 GitHub 搜索用户 yo-squirrel
觉得写得不错可以关注下微信公众号「松鼠专栏」
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。