- JSON Wen token,用于身份验证和身份交换
- 三部分组成:
-
Header (头部)
- 头部通常有两部分组成:令牌的类型(JWT)和 所使用签名的算法(如 HMAC SHA256 或 RSA)。
{ "alg": "HS256", "typ": "JWT" }
-
Payload (有效载荷)
- 有效载荷部分包含声明(claims),即存储在令牌中的信息。声明可以是注册的(如
iss
、exp
、sub
等)、公共的或私有的。
{ "sub": "1234567890", "name": "John Doe", "admin": true, "iat": 1516239022 }
- 有效载荷部分包含声明(claims),即存储在令牌中的信息。声明可以是注册的(如
-
Signature (签名)
- 为了保证令牌未被篡改,需要使用头部中指定的算法对编码后的头部和有效载荷进行签名。使用的密钥是由服务端保管的
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), your-256-bit-secret)
-
- JWT的工作流程
- 用户登录:用户通过登录表单提交验证(如用户名和密码)
- 生产JWT: 服务端言验证用户凭证后生成JWT,并将其发送给客户端(token)
- 客户端存储JWT : 客户端可以将JWT存储在本地存储或者Cookie中
- 请求保护资源:随后的请求中,客户端将JWT发送到服务器(通常在HTTP头的Authorization中)
- 服务端验证JWT: 服务器验证JWT有效性
JWT 的优点
- 自包含:JWT 包含了所有用户信息,避免了多次查询数据库。
- 跨域:JWT 可以在不同的域间使用,非常适合微服务架构。
- 无状态:服务器不需要保存会话信息,减轻了服务器的负担。
JWT 的缺点
- 安全性:如果密钥泄露,攻击者可以伪造有效的 JWT。使用 HTTPS 保护传输非常重要。
- 有效性:一旦生成,JWT 在到期前无法被撤销,因此需要合理设置有效期。
———————————————————————————————————————
代码例子:
// ***************生成token****************************
// 【实际上就是对uid进行加密】
const generateToken = function (uid, scope) {
// scope: 用户权限
const secretKey = global.config.security.secretKey
const expiresIn = global.config.security.expiresIn
// 接收三个参数:payload,secret,options
// payload:要加密的数据
// secret:密钥
// options:配置项(密钥过期时间等)
const token = jwt.sign(
{
// 要加密的数据:用户id,权限
uid,
scope,
},
secretKey,
{
expiresIn,
},
)
return token
}
// ***********************解析token ***********************
const basicAuth = require('basic-auth') // 用于处理HTTP Basic认证,从请求中提取用户名和密码
const jwt = require('jsonwebtoken')
class Auth {
constructor(level) {
// level是接口的层级
this.level = level || 1
// 用户权限,分级
Auth.USER = 8
Auth.ADMIN = 16
Auth.SUPER_ADMIN = 32
}
get m() {
return async (ctx, next) => {
const errMsg = 'token 不合法'
const userToken = basicAuth(ctx.req) // 获取用户名和密码
/**
* basicAuth(ctx.req)用于解析 HTTP Basic Authentication 的凭据。在 HTTP Basic Authentication 中,客户端在请求头中发送的凭据格式如下:
Authorization:Basic <base64(username:passsword)>
当你调用 `basicAuth(ctx.req)` 时,它会从请求的头部提取 `Authorization` 字段,并解析出用户名和密码。
在 `Auth` 类中,`userToken` 会被解析为:
{
name: 'username', // 解码后的用户名
pass: 'password' // 解码后的密码
}
*/
if (!userToken || !userToken.name) {
throw new global.errs.Forbidden(errMsg)
}
try {
var decode = jwt.verify(
userToken.name,
global.config.security.secretKey,
)
} catch (err) {
// token 不合法
// token 过期
if (err.name == 'TokenExpiredError') {
throw new global.errs.Forbidden('token 已过期')
}
throw new global.errs.Forbidden(errMsg)
}
if (decode.scope < this.level) {
throw new global.errs.Forbidden('权限不足')
}
// token 合法
ctx.auth = { ...decode }
await next()
}
}
static verifyToken(token) {
try {
jwt.verify(token, global.config.security.secretKey)
return true
} catch (err) {
return false
}
}
}
module.exports = {
Auth,
}
// 客户端传入的时候:
_encode(){
const token = wx.getStorageSync('token')
// 对token:进行base64, 这表明用户只需提供 token 作为身份凭证,而没有设置密码。
const base64 = Base64.encode(`${token}:`)
return 'Basic '+base64
},
-----
header:{
Authorization:this._encode()
},