JWT

发布于:2024-05-03 ⋅ 阅读:(22) ⋅ 点赞:(0)

啥也不做的请求

没有session、jwt image.png 这种情况服务器接收到的用户信息不知道是否被串改或伪造,没法信任

改进-签名

image.png 下面就是加密算法

const crypto = require('crypto'); // js内置的加密模块

const sign = (data, secret) => {
    // 加密算法为sha256,secret为秘钥
    const hmac = crypto.createHmac('sha256', secret);
    // 对信息进行签名
    hmac.update(data); 
    return hmac.digest('hex'); // 把签名结果转换成16进制字符串
};

console.log(sign('abc', '123'));

image.png

上图生成的字符串包含两个信息,一个是我们要加密的信息(abc),另一个是密钥(123),只要加密信息和密钥是一样的,这个结果是固定的

服务器把登陆信息和签名给浏览器,以后浏览器请求的时候带上这个,服务器就可以验证了,

服务器如何验证:服务器把对过来的信息里面的info重新进行签名看和发过来的内容是否一样,一样的话就是信息没有被串改过,这就防止了串改和伪造,因为加密算法和密钥都是在服务器上的

JWT

把上面 签名 进行标准化就是我们的 JWT

JWT由三部分组成(HEADER、PAYLOAD、VERIFY SIGNATURE),每部分由点分割 image.png

第一部分:header指定算法和类型,然后base64编码,在浏览器控制台上可以验证

image.png

第二部分:信息主体,比如服务器要颁发的用户ID,用户账号,uuid,jwt过期时间等等,然后base64编码,

第三部分:对前两部分 header.payload 进行签名,以点进行连接,签名完成后进行base64编码

小结

  • JWT可以有效解决信息验证问题,由于信息可以验证,所以信息可以被信任,所以登陆状态就可以保存在客户端了,所以服务器压力就减小了(过去的做法服务器还要建立一个session表格,现在不要了)

  • JWT的出现给我们状态的保存带来一种新的方案 -- 分布式存储的方案,服务器不再去存储状态了,而是把状态交给客户端进行存储

  • session + cookie:把状态数据保存到服务端,session id 放到 cookie 里返回,这样每次请求会带上 cookie ,通过 id 来查找到对应的 session。这种方案有 CSRF、分布式 session、跨域的问题。

  • jwt:把状态保存在 json 格式的 token 里,放到 header 中,需要手动带上,没有 cookie + session 的那些问题,但是也有安全性、性能、没法手动控制失效的问题。

  • session 因为是存在服务端的,那我们就可以随时让它失效JWT 不是,因为是保存在客户端,那我们是没法手动让他失效的。比如踢人、退出登录、改完密码下线这种功能就没法实现。但也可以配合 redis 来解决,记录下每个 token 对应的生效状态,每次先去 redis 查下 jwt 是否是可用的,这样就可以让 jwt 失效。

Nestjs使用

$ nest new jwt-test -p pnpm
$ cd jwt-test
$ pnpm install -S @nestjs/jwt

在 AppModule 里引入 JwtModule:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { JwtModule } from '@nestjs/jwt';

@Module({
  imports: [
    JwtModule.register({
      secret: 'xueyou',  // 密钥
      signOptions: {
        expiresIn: '10d' // 过期时间 10 天
      }
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

在 app.controller.ts 里注入 JwtService 并写 test 接口:

import { Controller, Get, Inject, Res, Headers, UnauthorizedException } from '@nestjs/common';
import { AppService } from './app.service';
import { JwtService } from '@nestjs/jwt';
import { Response } from 'express';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Inject(JwtService)
  private readonly jwtService: JwtService;

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
  @Get('test')
  test(@Headers('authorization') authorization: string, @Res({ passthrough: true}) response: Response) {
    if (authorization) {
      try {
        // 携带 jwt 需要加载 authorization 的 header 里,以 Bearer xxx 的格式
        const token = authorization.split(' ')[1];
        const data = this.jwtService.verify(token); // 校验并取出信息

        const newToken = this.jwtService.sign({
          count: data.count + 1,
        });
        // 返回 jwt 可以放在任何地方,header、cookie 或者 body 里都可以
        response.setHeader('token', newToken);
        return data.count + 1;
      } catch (e) {
        throw new UnauthorizedException();
      }
    } else {
      const newToken = this.jwtService.sign({
        count: 1,
      });

      response.setHeader('token', newToken);
      return 1;
    }
  }
}
$ npm run start:dev

每次把返回的 token 放到请求的 Authorization 里面,然后观察返回的值,每次加1,如果内容不对就会报错,如果没有更改,返回内容不会增加 image.png

image.png