>  기사  >  웹 프론트엔드  >  JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)

JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)

青灯夜游
青灯夜游앞으로
2022-10-28 19:37:291779검색

이 글에서는 백엔드 JWT 인증 관련 원칙과 이를 Node에서 사용하는 방법에 대해 설명하겠습니다. 감사합니다.

JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)

【관련 튜토리얼 추천: nodejs 동영상 튜토리얼

1. JWT를 사용하는 이유

한 기술의 출현은 다른 기술의 단점을 보완하기 위한 것입니다. JWT가 등장하기 전에는 세션 인증 메커니즘을 쿠키로 구현해야 했습니다. 쿠키는 기본적으로 교차 도메인 액세스를 지원하지 않으므로, 백엔드 인터페이스에 대한 프런트엔드 교차 도메인 요청의 경우 교차 도메인 세션 인증을 달성하려면 많은 추가 구성이 필요합니다.

참고:

  • 프런트 엔드가 백엔드 인터페이스를 요청하고 도메인 간 문제가 없는 경우 세션 인증 메커니즘을 사용하는 것이 좋습니다.
  • 프런트 엔드가 도메인 간에 백엔드 인터페이스를 요청해야 하는 경우 세션 인증 메커니즘을 사용하지 않는 것이 좋습니다. JWT 인증 메커니즘을 사용하는 것이 좋습니다.

2. JWT란?

JWT(정식 이름: JSON Web Token)는 현재 가장 널리 사용되는 도메인 간 인증 솔루션입니다. 그 핵심은 아래와 같이 문자열 쓰기 사양으로, 현재 프론트엔드와 백엔드 분리 개발 과정에서 token은 사용자와 서버 간에 안전하고 신뢰할 수 있는 정보를 전달하는 데 사용됩니다. 사용되는 인증 메커니즘은 신원 확인을 위한 가장 일반적인 솔루션입니다. 프로세스는 다음과 같습니다.

서버는 사용자 계정과 비밀번호가 올바른지 확인하면 사용자에게 토큰을 발급합니다. 사용자는 일부 인터페이스에 액세스할 수 있습니다

JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석) 후속 방문에서는 이 토큰을 사용하여 사용자에게 액세스 권한이 있는지 확인합니다

token鉴权机制用于身份验证是最常见的方案,流程如下:
  • 服务器当验证用户账号和密码正确的时候,给用户颁发一个令牌,这个令牌作为后续用户访问一些接口的凭证
  • 后续访问会根据这个令牌判断用户是否有权限进行访问

三、JWT工作原理

JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)

总结:用户的信息通过 Token 字符串的形式,保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用户的身份。

四、token的组成部分

Token,分成了三部分,头部(Header)、载荷(Payload)、签名(Signature),并以.进行拼接。其中头部和载荷都是以JSON格式存放数据,只是进行了编码。格式如下:

Header.Payload.Signature

下面是 JWT 字符串的示例:

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNjQ0ODI3NzI2LCJleHAiOjE2NDQ4Mjc3NTZ9.gdZKg9LkPiQZIgNAZ1Mn14GQd9kZZua-_unwHQoRsKE

注意:Bearer 是手动添加的头部信息,必须携带此信息才能解析token !

五、token的三个部分的含义

1. header

每个JWT都会带有头部信息,这里主要声明使用的算法。声明算法的字段名为alg,同时还有一个typ的字段,默认JWT即可。以下示例中算法为HS256:

{ "alg": "HS256", "typ": "JWT" }

因为JWT是字符串,所以我们还需要对以上内容进行Base64编码,编码后字符串如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

2. payload

载荷即消息体,这里会存放实际的内容,也就是Token的数据声明,例如用户的idname,默认情况下也会携带令牌的签发时间iat,通过还可以设置过期时间,如下:

{ 
   "sub": "1234567890", 
   "name": "CoderBin", 
   "iat": 1516239022
}

同样进行Base64编码后,字符串如下:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

3. Signature

签名是对头部和载荷内容进行签名,一般情况,设置一个secretKey,对前两个的结果进行HMACSHA25算法,公式如下:

Signature = HMACSHA256(base64Url(header)+.+base64Url(payload),secretKey)

一旦前面两部分数据被篡改,只要服务器加密用的密钥没有泄露,得到的签名肯定和之前的签名不一致

六、JWT使用方式

客户端收到服务器返回的 JWT 之后,通常会将它储存在 localStoragesessionStorage 中。

此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证。推荐的做法是把 JWT 放在 HTTP 请求头的 Authorization 字段中,格式如下:

Authorization: Bearer <token></token>

七、JWT实现

Token 的使用分成了两部分:

  • 生成token:登录成功的时候,颁发token
  • 验证token:访问某些资源或者接口时,验证token

接下来就在 node+express3 JWT 작동 원리

🎜🎜JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)🎜🎜요약: 사용자 정보는 브라우저에 토큰 문자열 형태로 클라이언트가 표시됩니다. 서버는 🎜복원🎜토큰 문자열 형식으로 사용자의 신원을 인증합니다. 🎜

🎜4. 토큰🎜🎜🎜토큰의 구성 요소는 헤더, 페이로드, 서명(서명)의 세 부분으로 나누어져 있습니다. .로. 헤더와 페이로드는 JSON 형식으로 데이터를 저장하지만 인코딩됩니다. 형식은 다음과 같습니다. 🎜
npm i jsonwebtoken express-jwt
🎜다음은 JWT 문자열의 예입니다. 🎜
// 导入用于生成 JWT 字符串的包
const jwt = require('jsonwebtoken')

// 导入用户将客户端发送过来的 JWT 字符串,解析还原成 JSON 对象的包
const expressJWT = require('express-jwt')
🎜참고: Bearer는 수동으로 추가된 헤더 정보이며, 토큰을 구문 분석하려면 이 정보를 전달해야 합니다! 🎜

🎜5. 토큰의 세 부분의 의미🎜🎜

🎜1. 헤더🎜

🎜각각. JWT 모두에는 주로 사용된 알고리즘을 선언하는 헤더 정보가 있습니다. 알고리즘을 선언하는 필드 이름은 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 字符串
  })
})

🎜 2. 페이로드🎜

🎜페이로드는 실제 콘텐츠가 저장될 메시지 본문으로, 사용자의 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 // 要发送给客户端的用户信息
  })
})

🎜3.Signature🎜

🎜Signature는 헤더와 페이로드 콘텐츠에 서명하는 것입니다. 일반적으로 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: '未知的错误'
  })
})
🎜데이터의 처음 두 부분이 변조되면 키가 서버 암호화에 사용된 서명이 유출되지 않으면 획득한 서명은 이전 서명과 확실히 일치하지 않습니다🎜

🎜6 JWT 사용 방법🎜🎜🎜클라이언트가 반환한 JWT를 받은 후 서버에서는 일반적으로 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')
})

🎜JWTimplementation🎜🎜🎜Token입니다. 사용법은 두 부분으로 나누어집니다. 🎜🎜🎜토큰 생성: 로그인에 성공하면 토큰이 발급됩니다.🎜🎜확인 토큰: 특정 리소스나 인터페이스에 접근할 때 토큰이 확인됩니다🎜🎜🎜다음, node+express 환경에서 JWT 인증을 구현하는 방법을 보여드리겠습니다. 마지막에는 주석이 포함된 완전한 코드가 있습니다🎜

1. 安装 JWT 相关的包

运行如下命令,安装如下两个 JWT 相关的包:

npm i jsonwebtoken express-jwt

其中:

  • jsonwebtoken 用于生成 JWT 字符串
  • express-jwt 用于验证token,将 JWT 字符串解析还原成 JSON 对象

2. 导入 JWT 相关的包

app.js

// 导入用于生成 JWT 字符串的包
const jwt = require('jsonwebtoken')

// 导入用户将客户端发送过来的 JWT 字符串,解析还原成 JSON 对象的包
const expressJWT = require('express-jwt')

3. 定义 secret 密钥 *

为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,我们需要专门定义一个用于加密解密的 secret 密钥:

  • 当生成 JWT 字符串的时候,需要使用 secret 密钥对用户的信息进行加密,最终得到加密好的 JWT 字符串

  • 当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行解密

// 这个 secretKey 的是可以是任意的字符串
const secretKey = 'CoderBin ^_^'

4. 在登录成功后生成 JWT 字符串 *

调用 jsonwebtoken 包提供的 sign() 方法,将用户的信息加密成 JWT 字符串,响应给客户端:

  • 参数 1:用户的信息对象
  • 参数 2:解密的秘钥
  • 参数 3:配置对象,可以配置 token 的有效期

注意:千万不要把密码加密到 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 字符串
  })
})

5. 将 JWT 字符串还原为 JSON 对象 *

客户端每次在访问那些有权限接口的时候,都需要主动通过请求头中的 Authorization 字段,将 Token 字符串发送到服务器进行身份认证。

此时,服务器可以通过 express-jwt 这个中间件,自动将客户端发送过来的 Token 解析还原成 JSON 对象:

  • express.JWT({ secret: secretKey, algorithms: ['HS256'] }) 就是用来解析 Token 的中间件
  • express-jwt 模块,现在默认为 6版本以上,必须加上: algorithms: ['HS256']

注意:只要配置成功了 express-jwt 这个中间件,就会自动把解析出来的用户信息,挂载到 req.user 属性上

// 1. 使用 app.use() 来注册中间件
app.use(expressJWT({ 
  secret: secretKey, 
  algorithms: ['HS256'] 
}).unless({ path: [/^\/api\//] }))

注意

  • secret 必须和 sign 时候保持一致
  • 可以通过 unless 配置接口白名单,也就是哪些 URL 可以不用经过校验,像登陆/注册都可以不用校验
  • 校验的中间件需要放在需要校验的路由前面,无法对前面的 URL 进行校验

6. 使用 req.user 获取用户信息

当 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 // 要发送给客户端的用户信息
  })
})

7. 捕获解析 JWT 失败后产生的错误

当使用 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: '未知的错误'
  })
})

8. 完整代码

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')
})

八. 测试结果

1 测试登录接口

借助 postman 工具测试接口

JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)

2. 测试登录需要权限的接口-失败

JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)

3. 测试登录需要权限的接口-成功

JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)

九、最后总结

JWT鉴权机制有许多优点:

  • json具有通用性,所以可以跨语言
  • 组成简单,字节占用小,便于传输
  • 服务端无需保存会话信息,很容易进行水平扩展
  • 一处生成,多处使用,可以在分布式系统中,解决单点登录问题
  • 可防护CSRF攻击

当然,不可避免的也有一些缺点:

  • 페이로드 부분은 단순히 인코딩되므로 논리에 필요한 중요하지 않은 정보를 저장하는 데에만 사용할 수 있습니다.
  • 암호화 키는 일단 유출되면 재앙을 초래할 수 있습니다. 토큰이 하이재킹되면 https 프로토콜을 사용하는 것이 가장 좋습니다
  • 이번 JWT 인증 메커니즘에 대한 간략한 분석은 모두에게 도움이 되기를 바랍니다. 감사합니다!

노드 관련 지식을 더 보려면

nodejs 튜토리얼

을 방문하세요!

위 내용은 JWT란 무엇입니까? Node에서 JWT 인증 메커니즘을 구현하는 방법(간략한 분석)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제