来源:Node.js

JWT

JWT(JSON Web Token)是一种开放的标准(RFC 7519),一种用于在网络应用间传递信息的方式

它是一种基于JSON的安全令牌,用于在客户端和服务器之间传输信息

JWT的组成

JWT由三部分组成,它们通过点(.)进行分隔

  • Header(头部):包含令牌的类型、使用的加密算法等信息,常用Base64编码表示
  • Payload(负载):包含身份验证和授权等信息,如用户ID、角色、权限等,也可以自定义其他相关信息,同样采用Base64编码表示
  • Signature(签名):使用指定密钥对头部和负载进行签名,以确保令牌的完整性和真实性

JWT的工作流程

  1. 用户通过提供有效的凭证(例如用户名和密码)进行身份验证
  2. 服务器验证凭证,并生成一个JWT作为响应,JWT包含了用户的身份信息和其他必要的数据
  3. 服务器将JWT发送给客户端
  4. 客户端在后续的请求中,将JWT放入请求的头部或其他适当的位置
  5. 服务器在接收到请求时,验证JWT的签名以确保其完整性和真实性,如果验证通过,服务器使用JWT中的信息进行授权和身份验证

使用

依赖

  • passport:用于身份验证和授权
  • passport-jwt:是passport库的一个插件,用于支持使用JWT进行身份验证和授权
  • jsonwebtoken:生成token
1
pnpm i passport passport-jwt jsonwebtoken @types/passport @types/passport-jwt @types/jsonwebtoken

代码

在src/jwt/index.ts编写jwt类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import { injectable } from "inversify"
import passport from 'passport'
import jsonwebtoken from 'jsonwebtoken'
import { Strategy, ExtractJwt } from 'passport-jwt'

@injectable()
export class JWT {

// 密钥
private secret = 'yajuesenpai114514'
// jwt验证策略配置
private jwtOptions = {
// 使用token的方式
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: this.secret
}


constructor() {
this.strategy()
}

/**
* 初始化jwt策略
*/
public strategy () {
let strategy = new Strategy(this.jwtOptions, (payload, done) => {
// payload:载荷 done:回调
// 鉴权成功
done('成功了', payload)
// 鉴权失败
// done(something, false)
})
// 注册jwt策略
passport.use(strategy)
}

/**
* 验证token
*/
static middleware () {
return passport.authenticate('jwt', { session: false })
}

/**
* 生成token
*/
public createToken (data: object) {
// 参数1:载荷 参数2:密钥 参数3:有效期
jsonwebtoken.sign({}, this.secret, { expiresIn: '7d' })
}

/**
* 集成到express
*/
public init () {
return passport.initialize()
}
}

在main.ts引入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import 'reflect-metadata'
import { InversifyExpressServer } from 'inversify-express-utils'
import { Container } from 'inversify'
import express from 'express'

import { PrismaClient } from '@prisma/client'
import { PrismaDB } from './src/db'

import { JWT } from './src/jwt'

import { User } from './src/user/controller'
import { UserService } from './src/user/service'

const container = new Container()

container.bind(User).to(User)
container.bind(UserService).to(UserService)

container.bind<PrismaClient>(PrismaClient).toFactory(()=> {
return ()=> {
return new PrismaClient()
}
})
container.bind(PrismaDB).to(PrismaDB)

//注入jwt
container.bind(JWT).to(JWT)

const server = new InversifyExpressServer(container)

server.setConfig(app => {
app.use(express.json())
// 关联JWT
app.use(container.get(JWT).init())
})
const app = server.build()

app.listen(11451, ()=> {
console.log('server on 11451')
})

// 声明token载荷的类型
declare global {
namespace Express {
interface User {
id: number,
name: string,
email: string
}
}
}

在src/user/service.ts生成token:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { injectable, inject } from "inversify"
import { PrismaDB } from "../db"

import { plainToClass } from 'class-transformer'
import { validate } from 'class-validator'
import { UserDto } from "./user.dto"

import { JWT } from "../jwt"
import { INJECT_TAG } from "inversify/lib/constants/metadata_keys"

@injectable()
export class UserService {

constructor(
@inject(PrismaDB) private readonly PrismaDB:PrismaDB,
@inject(JWT) private readonly jwt:JWT
) {

}

// 省略...

public async createUser(user: UserDto) {
const userDto = plainToClass(UserDto, user)
const errors = await validate(userDto)
if(errors.length) {
return errors
} else {
const result = await this.PrismaDB.prisma.user.create({
data: user
})
return {
...result,
// 生成token,传入用户信息作为载荷,并返回
token: this.jwt.createToken(result)
}
}
}
}

在src/user/controller.ts验证token:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { controller, httpGet as Get, httpPost as Post } from "inversify-express-utils"
import { UserService } from "./service"
import { inject } from "inversify"
import type { Request, Response } from "express"

import { JWT } from "../jwt"

@controller('/user')
export class User {

constructor(@inject(UserService) private readonly userService: UserService) {
}

// 装饰器里直接加入JWT的中间件,代表此方法必须经过token验证才可访问
@Get('/index', JWT.middleware())
public async getIndex(req: Request, res: Response) {
// 还可以直接读取到token信息
console.log(req.user.id)
let result = await this.userService.getList()
res.send(result)
}

// 省略...
}