이 글에서는 백엔드 JWT 인증 관련 원칙과 이를 Node에서 사용하는 방법에 대해 설명하겠습니다. 감사합니다.
【관련 튜토리얼 추천: nodejs 동영상 튜토리얼】
한 기술의 출현은 다른 기술의 단점을 보완하기 위한 것입니다. JWT가 등장하기 전에는 세션 인증 메커니즘을 쿠키로 구현해야 했습니다. 쿠키는 기본적으로 교차 도메인 액세스를 지원하지 않으므로, 백엔드 인터페이스에 대한 프런트엔드 교차 도메인 요청의 경우 교차 도메인 세션 인증을 달성하려면 많은 추가 구성이 필요합니다.
참고:
JWT(정식 이름: JSON Web Token)는 현재 가장 널리 사용되는 도메인 간 인증 솔루션입니다. 그 핵심은 아래와 같이 문자열 쓰기 사양으로, 현재 프론트엔드와 백엔드 분리 개발 과정에서 token
은 사용자와 서버 간에 안전하고 신뢰할 수 있는 정보를 전달하는 데 사용됩니다. 사용되는 인증 메커니즘은 신원 확인을 위한 가장 일반적인 솔루션입니다. 프로세스는 다음과 같습니다.
후속 방문에서는 이 토큰을 사용하여 사용자에게 액세스 권한이 있는지 확인합니다
token
鉴权机制用于身份验证是最常见的方案,流程如下:总结:用户的信息通过 Token 字符串的形式,保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用户的身份。
Token
,分成了三部分,头部(Header)、载荷(Payload)、签名(Signature),并以.
进行拼接。其中头部和载荷都是以JSON
格式存放数据,只是进行了编码。格式如下:
Header.Payload.Signature
下面是 JWT 字符串的示例:
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNjQ0ODI3NzI2LCJleHAiOjE2NDQ4Mjc3NTZ9.gdZKg9LkPiQZIgNAZ1Mn14GQd9kZZua-_unwHQoRsKE
注意:Bearer 是手动添加的头部信息,必须携带此信息才能解析token !
每个JWT都会带有头部信息,这里主要声明使用的算法。声明算法的字段名为alg
,同时还有一个typ
的字段,默认JWT
即可。以下示例中算法为HS256:
{ "alg": "HS256", "typ": "JWT" }
因为JWT是字符串,所以我们还需要对以上内容进行Base64编码,编码后字符串如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
载荷即消息体,这里会存放实际的内容,也就是Token
的数据声明,例如用户的id
和name
,默认情况下也会携带令牌的签发时间iat
,通过还可以设置过期时间,如下:
{ "sub": "1234567890", "name": "CoderBin", "iat": 1516239022 }
同样进行Base64编码后,字符串如下:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
签名是对头部和载荷内容进行签名,一般情况,设置一个secretKey
,对前两个的结果进行HMACSHA25
算法,公式如下:
Signature = HMACSHA256(base64Url(header)+.+base64Url(payload),secretKey)
一旦前面两部分数据被篡改,只要服务器加密用的密钥没有泄露,得到的签名肯定和之前的签名不一致
客户端收到服务器返回的 JWT 之后,通常会将它储存在 localStorage
或 sessionStorage
中。
此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证。推荐的做法是把 JWT 放在 HTTP 请求头的 Authorization 字段中,格式如下:
Authorization: Bearer <token></token>
Token
的使用分成了两部分:
接下来就在 node+express
3 JWT 작동 원리
토큰
의 구성 요소는 헤더, 페이로드, 서명(서명)의 세 부분으로 나누어져 있습니다. .
로. 헤더와 페이로드는 JSON
형식으로 데이터를 저장하지만 인코딩됩니다. 형식은 다음과 같습니다. 🎜npm i jsonwebtoken express-jwt🎜다음은 JWT 문자열의 예입니다. 🎜
// 导入用于生成 JWT 字符串的包 const jwt = require('jsonwebtoken') // 导入用户将客户端发送过来的 JWT 字符串,解析还原成 JSON 对象的包 const expressJWT = require('express-jwt')🎜참고: Bearer는 수동으로 추가된 헤더 정보이며, 토큰을 구문 분석하려면 이 정보를 전달해야 합니다! 🎜
alg
이며, typ
필드도 있습니다. 기본값은 JWT
입니다. 다음 예의 알고리즘은 HS256입니다. 🎜// 这个 secretKey 的是可以是任意的字符串 const secretKey = 'CoderBin ^_^'🎜JWT는 문자열이므로 위 콘텐츠도 Base64로 인코딩해야 합니다. 인코딩된 문자열은 다음과 같습니다. 🎜
// 登录接口 app.post('/api/login', function (req, res) { // 将 req.body 请求体中的数据,转存为 userinfo 常量 const userinfo = req.body // 省略登录失败情况下的代码... // 登录成功 // 在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端 const tokenStr = jwt.sign( { username: userinfo.username }, secretKey, { expiresIn: '30s' } ) // 向客户端响应成功的消息 res.send({ status: 200, message: '登录成功!', token: tokenStr // 要发送给客户端的 token 字符串 }) })
id
와 같은 Token
의 데이터 선언입니다. > 및 name
, 기본적으로 토큰 발급 시간 iat
도 전달하며 이를 통해 다음과 같이 만료 시간을 설정할 수도 있습니다. 🎜// 1. 使用 app.use() 来注册中间件 app.use(expressJWT({ secret: secretKey, algorithms: ['HS256'] }).unless({ path: [/^\/api\//] }))🎜동일한 Base64 인코딩 후 , 문자열은 다음과 같습니다. 🎜
// 这是一个有权限的 API 接口,必须在 Header 中携带 Authorization 字段,值为 token,才允许访问 app.get('/admin/getinfo', function (req, res) { // TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端 console.log(req.user); res.send({ status: 200, message: '获取用户信息成功!', data: req.user // 要发送给客户端的用户信息 }) })
secretKey를 사용하여 처음 두 부분에 서명 결과는 <code>HMACSHA25
알고리즘을 따르며 공식은 다음과 같습니다. 🎜app.use((err, req, res, next) => { // 这次错误是由 token 解析失败导致的 if (err.name === 'UnauthorizedError') { return res.send({ status: 401, message: '无效的token' }) } res.send({ status: 500, message: '未知的错误' }) })🎜데이터의 처음 두 부분이 변조되면 키가 서버 암호화에 사용된 서명이 유출되지 않으면 획득한 서명은 이전 서명과 확실히 일치하지 않습니다🎜
localStorage
또는 sessionStorage
중간에 저장합니다. 🎜🎜이후 클라이언트는 서버와 통신할 때마다 신원 인증을 위해 이 JWT 문자열을 가져와야 합니다. 권장되는 접근 방식은 HTTP 요청 헤더의 Authorization 필드에 JWT를 넣는 것입니다. 형식은 🎜// 导入 express 模块 const express = require('express') // 创建 express 的服务器实例 const app = express() // TODO_01:安装并导入 JWT 相关的两个包,分别是 jsonwebtoken 和 express-jwt const jwt = require('jsonwebtoken') const expressJWT = require('express-jwt') // 允许跨域资源共享 const cors = require('cors') app.use(cors()) // 解析 post 表单数据的中间件 const bodyParser = require('body-parser') // 这里用内置的中间件也行: app.use(express.urlencoded({ extended: false })) app.use(bodyParser.urlencoded({ extended: false })) // TODO_02:定义 secret 密钥,建议将密钥命名为 secretKey // 这个 secretKey 的是可以是任意的字符串 const secretKey = 'smiling ^_^' // TODO_04:注册将 JWT 字符串解析还原成 JSON 对象的中间件 // 1. 使用 app.use() 来注册中间件 // 2. express.JWT({ secret: secretKey, algorithms: ['HS256'] }) 就是用来解析 Token 的中间件 // 2.1 express-jwt 模块,现在默认为 6版本以上,必须加上: algorithms: ['HS256'] // 3. .unless({ path: [/^\/api\//] }) 用来指定哪些接口不需要访问权限 // 4. 注意:只要配置成功了 express-jwt 这个中间件,就会自动把解析出来的用户信息,挂载到 req.user 属性上 app.use(expressJWT({ secret: secretKey, algorithms: ['HS256'] }).unless({ path: [/^\/api\//] })) // 登录接口 app.post('/api/login', function (req, res) { // 将 req.body 请求体中的数据,转存为 userinfo 常量 const userinfo = req.body // 登录失败 if (userinfo.username !== 'admin' || userinfo.password !== '000000') { return res.send({ status: 400, message: '登录失败!' }) } // 登录成功 // TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端 // 参数 1:用户的信息对象 // 参数 2:解密的秘钥 // 参数 3:配置对象,可以配置 token 的有效期 // 记住:千万不要把密码加密到 token 字符串中! const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' }) res.send({ status: 200, message: '登录成功!', token: tokenStr // 要发送给客户端的 token 字符串 }) }) // 这是一个有权限的 API 接口,必须在 Header 中携带 Authorization 字段,值为 token,才允许访问 app.get('/admin/getinfo', function (req, res) { // TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端 console.log(req.user); res.send({ status: 200, message: '获取用户信息成功!', data: req.user // 要发送给客户端的用户信息 }) }) // TODO_06:使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误 app.use((err, req, res, next) => { // 这次错误是由 token 解析失败导致的 if (err.name === 'UnauthorizedError') { return res.send({ status: 401, message: '无效的token' }) } res.send({ status: 500, message: '未知的错误' }) }) // 调用 app.listen 方法,指定端口号并启动web服务器 app.listen(8888, function () { console.log('Express server running at http://127.0.0.1:8888') })
Token입니다.
사용법은 두 부분으로 나누어집니다. 🎜🎜🎜토큰 생성: 로그인에 성공하면 토큰이 발급됩니다.🎜🎜확인 토큰: 특정 리소스나 인터페이스에 접근할 때 토큰이 확인됩니다🎜🎜🎜다음, node+express 환경에서 JWT 인증을 구현하는 방법을 보여드리겠습니다. 마지막에는 주석이 포함된 완전한 코드가 있습니다🎜运行如下命令,安装如下两个 JWT 相关的包:
npm i jsonwebtoken express-jwt
其中:
jsonwebtoken
用于生成 JWT 字符串
express-jwt
用于验证token,将 JWT 字符串解析还原成 JSON 对象在app.js
中
// 导入用于生成 JWT 字符串的包 const jwt = require('jsonwebtoken') // 导入用户将客户端发送过来的 JWT 字符串,解析还原成 JSON 对象的包 const expressJWT = require('express-jwt')
为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,我们需要专门定义一个用于加密和解密的 secret 密钥:
当生成 JWT 字符串的时候,需要使用 secret 密钥对用户的信息进行加密,最终得到加密好的 JWT 字符串
当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行解密
// 这个 secretKey 的是可以是任意的字符串 const secretKey = 'CoderBin ^_^'
调用 jsonwebtoken 包提供的 sign()
方法,将用户的信息加密成 JWT 字符串,响应给客户端:
注意:千万不要把密码加密到 token 字符串中!
// 登录接口 app.post('/api/login', function (req, res) { // 将 req.body 请求体中的数据,转存为 userinfo 常量 const userinfo = req.body // 省略登录失败情况下的代码... // 登录成功 // 在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端 const tokenStr = jwt.sign( { username: userinfo.username }, secretKey, { expiresIn: '30s' } ) // 向客户端响应成功的消息 res.send({ status: 200, message: '登录成功!', token: tokenStr // 要发送给客户端的 token 字符串 }) })
客户端每次在访问那些有权限接口的时候,都需要主动通过请求头中的 Authorization 字段,将 Token 字符串发送到服务器进行身份认证。
此时,服务器可以通过 express-jwt 这个中间件,自动将客户端发送过来的 Token 解析还原成 JSON 对象:
express.JWT({ secret: secretKey, algorithms: ['HS256'] })
就是用来解析 Token 的中间件algorithms: ['HS256']
注意:只要配置成功了 express-jwt 这个中间件,就会自动把解析出来的用户信息,挂载到 req.user 属性上
// 1. 使用 app.use() 来注册中间件 app.use(expressJWT({ secret: secretKey, algorithms: ['HS256'] }).unless({ path: [/^\/api\//] }))
注意:
当 express-jwt 这个中间件配置成功之后,即可在那些有权限的接口中,使用 req.user
对象,来访问从 JWT 字符串中解析出来的用户信息了,示例代码如下:
// 这是一个有权限的 API 接口,必须在 Header 中携带 Authorization 字段,值为 token,才允许访问 app.get('/admin/getinfo', function (req, res) { // TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端 console.log(req.user); res.send({ status: 200, message: '获取用户信息成功!', data: req.user // 要发送给客户端的用户信息 }) })
当使用 express-jwt 解析 Token 字符串时,如果客户端发送过来的 Token 字符串过期或不合法,会产生一个解析失败的错误,影响项目的正常运行。我们可以通过 Express 的错误中间件,捕获这个错误并进行相关的处理,示例代码如下:
app.use((err, req, res, next) => { // 这次错误是由 token 解析失败导致的 if (err.name === 'UnauthorizedError') { return res.send({ status: 401, message: '无效的token' }) } res.send({ status: 500, message: '未知的错误' }) })
app.js
// 导入 express 模块 const express = require('express') // 创建 express 的服务器实例 const app = express() // TODO_01:安装并导入 JWT 相关的两个包,分别是 jsonwebtoken 和 express-jwt const jwt = require('jsonwebtoken') const expressJWT = require('express-jwt') // 允许跨域资源共享 const cors = require('cors') app.use(cors()) // 解析 post 表单数据的中间件 const bodyParser = require('body-parser') // 这里用内置的中间件也行: app.use(express.urlencoded({ extended: false })) app.use(bodyParser.urlencoded({ extended: false })) // TODO_02:定义 secret 密钥,建议将密钥命名为 secretKey // 这个 secretKey 的是可以是任意的字符串 const secretKey = 'smiling ^_^' // TODO_04:注册将 JWT 字符串解析还原成 JSON 对象的中间件 // 1. 使用 app.use() 来注册中间件 // 2. express.JWT({ secret: secretKey, algorithms: ['HS256'] }) 就是用来解析 Token 的中间件 // 2.1 express-jwt 模块,现在默认为 6版本以上,必须加上: algorithms: ['HS256'] // 3. .unless({ path: [/^\/api\//] }) 用来指定哪些接口不需要访问权限 // 4. 注意:只要配置成功了 express-jwt 这个中间件,就会自动把解析出来的用户信息,挂载到 req.user 属性上 app.use(expressJWT({ secret: secretKey, algorithms: ['HS256'] }).unless({ path: [/^\/api\//] })) // 登录接口 app.post('/api/login', function (req, res) { // 将 req.body 请求体中的数据,转存为 userinfo 常量 const userinfo = req.body // 登录失败 if (userinfo.username !== 'admin' || userinfo.password !== '000000') { return res.send({ status: 400, message: '登录失败!' }) } // 登录成功 // TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端 // 参数 1:用户的信息对象 // 参数 2:解密的秘钥 // 参数 3:配置对象,可以配置 token 的有效期 // 记住:千万不要把密码加密到 token 字符串中! const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' }) res.send({ status: 200, message: '登录成功!', token: tokenStr // 要发送给客户端的 token 字符串 }) }) // 这是一个有权限的 API 接口,必须在 Header 中携带 Authorization 字段,值为 token,才允许访问 app.get('/admin/getinfo', function (req, res) { // TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端 console.log(req.user); res.send({ status: 200, message: '获取用户信息成功!', data: req.user // 要发送给客户端的用户信息 }) }) // TODO_06:使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误 app.use((err, req, res, next) => { // 这次错误是由 token 解析失败导致的 if (err.name === 'UnauthorizedError') { return res.send({ status: 401, message: '无效的token' }) } res.send({ status: 500, message: '未知的错误' }) }) // 调用 app.listen 方法,指定端口号并启动web服务器 app.listen(8888, function () { console.log('Express server running at http://127.0.0.1:8888') })
借助 postman 工具测试接口
JWT鉴权机制有许多优点:
当然,不可避免的也有一些缺点:
노드 관련 지식을 더 보려면
nodejs 튜토리얼위 내용은 JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!