Nest framework token login and token verification

1. Build the project

  • Project initialization:
npm i -g @nestjs/cli
nest new project-name


 

  • Execute these four commands in the terminal to generate two new modules:
nest g module auth 
nest g service auth
nest g module users 
nest g service users
  •  Then deleting these three files is useless:

UserModule

Write UsersService:

import { Injectable } from '@nestjs/common';

interface User {
    userId: number;
    username: string;
    password: string;
}

@Injectable()
export class UsersService {
    private readonly users: User[];

    constructor() {
        // 这里把用户列表写死了. 在真正的应用程序中,用户列表应该从数据库获取
        this.users = [
            {
                userId: 1,
                username: 'john',
                password: 'riddle',
            },
            {
                userId: 2,
                username: 'chris',
                password: 'secret',
            },
            {
                userId: 3,
                username: 'maria',
                password: 'guess',
            },
        ];
    }

    async findOne(username: string) {
        return this.users.find((user) => user.username === username);
    }
}

In UsersModule, export UsersService for use by other modules:

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';

@Module({
    providers: [UsersService],
    exports: [UsersService], // 导出 UsersService
})
export class UsersModule {}

AuthModule

Import UserModule in AuthModule:

import { Module } from '@nestjs/common';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';

@Module({
    imports: [UsersModule], // 导入 UsersModule
    providers: [AuthService],
})
export class AuthModule {}

Write AuthService:

import { Injectable } from '@nestjs/common';
import { UsersService } from 'src/users/users.service';

@Injectable()
export class AuthService {
    constructor(private readonly usersService: UsersService) {}

    /* 检查用户是否已存在 + 校验密码 */
    async validateUser(username: string, pwd: string) {
        const user = await this.usersService.findOne(username); // 获取用户
        if (user && user.password === pwd) {
            const { password, ...result } = user; // 剔除 password
            return result; // 返回用户信息
        }
        return null; // 用户不存在 / 密码错误
    }
}

2. Local strategy (this step is not jwt yet, this is just login authentication)

在终端上执行这两行代码:
npm i @nestjs/passport passport passport-local
npm i -D @types/passport-local

Add a new configuration file for writing local strategy in the auth directorylocal.strategy.ts< /span>

import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable() // 通过 PassportStrategy 使用 local 策略
export class LocalStrategy extends PassportStrategy(Strategy) {
    constructor(private readonly authService: AuthService) {
        super();
    }

    async validate(username: string, password: string) {
        const user = await this.authService.validateUser(username, password);
        if (!user) {
            throw new UnauthorizedException(); // 返回 '未授权' 错误 (401)
        }
        return user; // 返回用户信息
    }
}

Configure the AuthModule to use the Passport attribute just defined:

import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport/dist';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';

@Module({
    imports: [UsersModule, PassportModule], // 引入 PassportModule
    providers: [AuthService, LocalStrategy], // 注册 LocalStrategy
})
export class AuthModule {}

Login test

You can now implement a simple /login route and apply the built-in guards to start the Passport-local flow

import { Controller, Req, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport/dist/auth.guard';
import { Request } from 'express';

@Controller()
export class AppController {
    @UseGuards(AuthGuard('local')) // 启用本地策略
    @Post('login')
    async getHello(@Req() request: Request) {
        // Passport 会根据 validate() 方法的返回值创建一个 user 对象
        // 并以 req.user 的形式分配给请求对象
        return request.user;
    }
}

Current project structure:

Start project testing:

Code description:

        Add this annotation @UseGuards(AuthGuard('local')) // Enable local strategy, then when requesting '/login', the framework will first execute the class that inherits PassportStrategy The validate method

        ​​​​​Note: If it is not written here, it will default to local


PassportStrategy class  Local strategy The parameters of the validate method I personally think are variable , but it has not been implemented. After the validate method is completed, the result will be placed in request.user

3.Token strategy

npm i @nestjs/jwt passport-jwt
npm i @types/passport-jwt -D

 Introduce JwtModule in AuthModule:

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport/dist';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';

@Module({
    imports: [UsersModule, PassportModule, JwtModule], // 引入 JwtModule
    providers: [AuthService, LocalStrategy],
})
export class AuthModule {}

Write AuthService and add login() method:

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt/dist';
import { UsersService } from 'src/users/users.service';

@Injectable()
export class AuthService {
    constructor(
        private readonly usersService: UsersService,
        private readonly jwtService: JwtService,
    ) {}

    async validateUser(username: string, pwd: string) {
        const user = await this.usersService.findOne(username);
        if (user && user.password === pwd) {
            const { password, ...result } = user;
            return result;
        }
        return null;
    }

    async login(user: any) {
        const payload = { username: user.username, sub: user.userId };
        return {
            // 使用 jwtService.sign() 基于 payload 生成 token 字符串
            access_token: this.jwtService.sign(payload), 
        };
    }
}

Update auth.module.ts, configure JwtModule, and export AuthService: 

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport/dist';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    /* 配置 JwtModule */
    JwtModule.register({
      secret: 'ceshi', // 使用 token 签名密文,我在这里是写固定的,实际开发里面,不能这样
      signOptions: { expiresIn: '7d' }, // 设置 token 的有效期,七天
    }),
  ],
  providers: [AuthService, LocalStrategy],
  exports: [AuthService], // 导出 AuthService
})
export class AuthModule {}

For more information about Nest JwtModule see GitHub - @nestjs/jwt

For more information about available configuration options see GitHub - node-jsonwebtoken

Update the /login route to return the JWT:

import { Controller, Req, Post, UseGuards } from '@nestjs/common';
import { Request } from 'express';
import { AuthGuard } from '@nestjs/passport/dist/auth.guard';
import { AuthService } from './auth/auth.service';

@Controller()
export class AppController {
    constructor(private readonly authService: AuthService) {} // 依赖注入

    @UseGuards(AuthGuard('local'))
    @Post('login')
    async getHello(@Req() request: Request) {
        return this.authService.login(request.user); // 调用 login 方法
    }
}

Check token

Add the configuration file jwt.strategy.ts for writing local strategies in the auth directory.

import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromHeader('token'),
      ignoreExpiration: false,
      secretOrKey: 'ceshi', // 使用 token 签名密文来解密,我在这里是写固定的,实际开发里面,不能这样
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

3.1 Code description

Add JwtStrategy as provider in AuthModule:

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport/dist';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { LocalStrategy } from './local.strategy';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register({
      secret: 'ceshi', // 使用 token 签名密文,我在这里是写固定的,实际开发里面,不能这样
      signOptions: { expiresIn: '7d' },
    }),
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy], // 注册 JwtStrategy
  exports: [AuthService],
})
export class AuthModule {}

Update the app.controller.ts file to use JWT:

import { Controller, Req, Post, UseGuards, Get } from '@nestjs/common';
import { Request } from 'express';
import { AuthGuard } from '@nestjs/passport/dist/auth.guard';
import { AuthService } from './auth/auth.service';

@Controller()
export class AppController {
    constructor(private readonly authService: AuthService) {}

    @UseGuards(AuthGuard('local'))
    @Post('login')
    async getHello(@Req() request: Request) {
        return this.authService.login(request.user);
    }

    @UseGuards(AuthGuard('jwt')) // 使用 JWT 鉴权
    @Get('profile')
    getProfile(@Req() request: Request) {
        return request.user; // 返回用户信息
    }
}

Default policy 

In AppController, using the @UseGuards(AuthGuard(XXX)) decorator requires passing the name of the strategy XXX. We can declare a default policy so we don't have to pass in a name.

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport/dist';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';
import { jwtConstants } from './constants';
import { JwtStrategy } from './jwt.strategy';
import { LocalStrategy } from './local.strategy';

@Module({
    imports: [
        UsersModule,
        PassportModule.register({ defaultStrategy: 'jwt' }), // 配置默认策略
        JwtModule.register({
            secret: jwtConstants.secret,
            signOptions: { expiresIn: '60s' },
        }),
    ],
    providers: [AuthService, LocalStrategy, JwtStrategy],
    exports: [AuthService],
})
export class AuthModule {}

Sample code:

Sample code can be downloaded from here

Guess you like

Origin blog.csdn.net/qq_26112725/article/details/134996372