美文网首页
nest.js 集成 auth 鉴权 JWT

nest.js 集成 auth 鉴权 JWT

作者: 吴占超 | 来源:发表于2023-02-08 09:45 被阅读0次

身份验证是大多数应用程序的重要组成部分。有许多不同的方法和策略来处理身份验证。任何项目所采用的方法都取决于其特定的应用需求。本章介绍了几种可以适应各种不同要求的身份验证方法。

Passport是最流行的 node.js 身份验证库,在社区中广为人知,并成功用于许多生产应用程序。使用该模块将此库与Nest应用程序集成起来非常简单。@nestjs/passport在高层次上,Passport 执行一系列步骤来:

  • 通过验证用户的“凭据”(例如用户名/密码、JSON Web 令牌 ( JWT ) 或来自身份提供者的身份令牌)对用户进行身份验证
  • 管理经过身份验证的状态(通过发布便携式令牌,例如 JWT,或创建Express 会话
  • 将有关经过身份验证的用户的信息附加到Request对象,以便在路由处理程序中进一步使用

Passport 具有丰富的策略生态系统,可实现各种身份验证机制。虽然概念简单,但您可以选择的 Passport 策略集非常丰富且种类繁多。Passport 将这些不同的步骤抽象为一个标准模式,该@nestjs/passport模块将此模式包装并标准化为熟悉的 Nest 结构。

在本章中,我们将使用这些强大而灵活的模块为 RESTful API 服务器实现一个完整的端到端身份验证解决方案。您可以使用此处描述的概念来实施任何 Passport 策略来自定义您的身份验证方案。您可以按照本章中的步骤来构建这个完整的示例。您可以在此处找到包含完整示例应用程序的存储库。

目标

  • webapi登录接口获取jwt
  • 请求验证
  • 获取当前登录对象

安装依赖

$ yarn add @nestjs/passport passport passport-local passport-jwt @nestjs/jwt  crypto-js
$ yarn add @types/passport-local -D   
$ yarn add @types/crypto-js -D      
$ yarn add @types/passport-jwt -D

基础辅助类

  • /src/utils/const-config.ts
/**
 * 配置常量字符串
 */
export const CONST_CONFIG = {
  /**
   * 端口
   */
  PORT: 'port',
  /**
   * jwt secret
   */
  JWTSECRET: 'jwtsecret',
  /**
   * java请求地址
   */
  JAVASERVERURL: 'javaServerUrl',
  /**
   * 路由地址前缀
   */
  PREFIX: 'prefix',
};

  • /src/utils/aes-secret.ts
import CryptoJS from 'crypto-js';

const key = CryptoJS.enc.Utf8.parse('i8761286317826ABCDEF'); //十六位十六进制数作为密钥
const iv = CryptoJS.enc.Utf8.parse('fasdo978ouiojiocsdj'); //十六位十六进制数作为密钥偏移量

/**
 * 解密
 * @param word
 * @returns
 */
export const secretDecrypt = (word: string) => {
  const encryptedHexStr = CryptoJS.enc.Hex.parse(word);
  const srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
  const decrypt = CryptoJS.AES.decrypt(srcs, key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7,
  });
  const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
  return decryptedStr.toString();
};

/**
 * 加密
 * @param word
 * @returns
 */
export const secretEncrypt = (word: string) => {
  const srcs = CryptoJS.enc.Utf8.parse(word);
  const encrypted = CryptoJS.AES.encrypt(srcs, key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7,
  });
  return encrypted.ciphertext.toString().toUpperCase();
};

创建auth module

$ nest g module auth
$ nest g service auth
$ nest g controller auth
  • /src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { InjectModel } from '@nestjs/sequelize';
import { USER, UserModel } from 'src/model/customer/user.model';
import { User } from 'src/user/entities/user.entity';
import { secretEncrypt } from 'src/utils/aes-secret';
import { CONST_CONFIG } from 'src/utils/const-config';

@Injectable()
export class AuthService {
  constructor(
    @InjectModel(UserModel)
    private userModel: typeof UserModel,
    private jwtService: JwtService,
    private configService: ConfigService,
  ) {}

  /**
   * 用户名密码校验
   * @param username
   * @param password
   * @returns
   */
  async validateUser(username: string, password: string): Promise<User> {
    const secretPwd = secretEncrypt(password);
    const user = await this.userModel.findOne({
      where: {
        [USER.USERNAME]: username.trim(),
        [USER.PASSWORD]: secretPwd,
      },
    });
    if (!user) {
      return user;
    }
    return null;
  }

  /**
   * 登录
   * @param user
   * @returns
   */
  async login(user: User) {
    const payload = {
      username: user.username,
      userId: user.id,
    };
    return {
      accessToken: this.jwtService.sign(payload, {
        secret: this.configService.get<string>(CONST_CONFIG.JWTSECRET),
        expiresIn: '7d',
      }),
      user: {
        id: user.id,
        phoneNumber: user.phoneNumber,
        userName: user.username,
      },
    };
  }
}

本地策略

策略使用方法 @UseGuards(LocalGuard) 根据接口场景采用不同策略

本地策略指本地登录策略(用户用户名密码请求认证,返回jwt 后续认证走jwt策略)

  • /src/auth/local.strategy.ts
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { AuthService } from './auth.service';
import { User } from 'src/user/entities/user.entity';

/**
 * 本地登录策略
 */
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super();
  }

  async validate(username: string, password: string): Promise<User> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new HttpException('用户名或者密码错误!', HttpStatus.UNAUTHORIZED);
    }
    return user;
  }
}

我们可以在调用中传递一个选项对象来自super()定义护照策略的行为。在此示例中,默认情况下,护照本地策略需要在请求正文中调用username和的属性。password传递一个选项对象来指定不同的属性名称,例如:super({ usernameField: 'email' }). 有关详细信息,请参阅Passport 文档

  • /src/auth/jwt.strategy.ts
    jwt策略是指请求的校验方式采用jwt校验(登录后校验)
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { CONST_CONFIG } from 'src/utils/const-config';

/**
 * jwt 校验策略
 */
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private configService: ConfigService) {
    const secret = configService.get<string>(CONST_CONFIG.JWTSECRET);
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: secret,
    });
  }

  async validate(payload: any) {
    console.log(payload);
    return payload;
  }
}

api

post -> /auth/login ->加载本地策略(local-auth.guard.ts)-> 牌照校验(local.strategy.ts)validate -> auth.controller.login(req 被牌照校验后返回值替换)-> return result

  • /src/user/auth.controller.ts
import {
  Controller,
  HttpException,
  HttpStatus,
  Post,
  Req,
  UseGuards,
} from '@nestjs/common';
import { User } from 'src/user/entities/user.entity';
import { AuthService } from './auth.service';
import { LocalAuthGuard } from './local-auth.guard';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}
  
  @UseGuards(LocalAuthGuard)
  @Post('/login')
  async login(@Req() req: { user: User }) {
    const result = this.authService.login(req.user);
    if (result) {
      return result;
    }
    throw new HttpException('用户名或者密码错误!', HttpStatus.FORBIDDEN);
  }
}

graphql 使用

  • /src/auth/current-user.ts
    graphql resolver 请求参数获取
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';

/**
 * 自定义参数装饰器
 */
export const CurrentUser = createParamDecorator(
  (data: unknown, context: ExecutionContext) => {
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req.user;
  },
);

  • /src/auth/gql-auth.guard.ts
import { ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req;
  }
}

  • /src/user/user.resolver.ts
import {
  Resolver,
  Query,
  Mutation,
  Args,
  Info,
  Parent,
  ResolveField,
} from '@nestjs/graphql';
import { UserService } from './user.service';
import { User } from './entities/user.entity';
import { CreateUserInput } from './dto/create-user.input';
import { UpdateUserInput } from './dto/update-user.input';
import { FindAllInput } from 'src/utils/common.input';
import { UseGuards } from '@nestjs/common';
import { GqlAuthGuard } from 'src/auth/gql-auth.guard';
import { CurrentUser } from 'src/auth/current-user';
import { JwtAuthEntity } from 'src/auth/jwt-auth-entity';
import { OrgroleUser } from 'src/orgrole-user/entities/orgrole-user.entity';
import { OrgroleUserService } from 'src/orgrole-user/orgrole-user.service';

@Resolver(() => User)
export class UserResolver {
  constructor(
    private readonly userService: UserService,
    private readonly orgroleUserService: OrgroleUserService,
  ) {}

  @UseGuards(GqlAuthGuard)
  @Mutation(() => User)
  createUser(
    @Args('createUserInput') createUserInput: CreateUserInput,
    @CurrentUser() user: JwtAuthEntity,
  ) {
    return this.userService.create(createUserInput, user);
  }

  @UseGuards(GqlAuthGuard)
  @Query(() => [User], { name: 'UserAll' })
  findAll(
    @Args('param') param: FindAllInput,
    @CurrentUser() user: JwtAuthEntity,
  ) {
    return this.userService.findAll(param, user);
  }

  @UseGuards(GqlAuthGuard)
  @Query(() => User, { name: 'User' })
  findOne(
    @Args('id', { type: () => String }) id: string,
    @CurrentUser() user: JwtAuthEntity,
  ) {
    return this.userService.findByPk(id, user);
  }

  @UseGuards(GqlAuthGuard)
  @Mutation(() => User)
  updateUser(
    @Args('updateUserInput') updateUserInput: UpdateUserInput,
    @CurrentUser() user: JwtAuthEntity,
  ) {
    return this.userService.update(updateUserInput.id, updateUserInput, user);
  }

  @UseGuards(GqlAuthGuard)
  @Mutation(() => User)
  removeUser(
    @Args('id', { type: () => String }) id: string,
    @CurrentUser() user: JwtAuthEntity,
  ) {
    return this.userService.remove(id, user);
  }

  @ResolveField(() => [OrgroleUser], { nullable: true })
  async orgroleUserUserId(
    @Parent() parent: User, // Resolved object that implements Character
    @Info() { info }, // Type of the object that implements Character
    @Args('param', { type: () => FindAllInput, nullable: true })
    param: FindAllInput,
  ) {
    if (parent.id) {
      return undefined;
    }
    // Get character's friends
    return this.orgroleUserService.findAll({
      ...param,
      where: {
        userId: parent.id,
        ...param?.where,
      },
    });
  }
}

相关文章

  • nest.js 集成 auth 鉴权 JWT

    身份验证是大多数应用程序的重要组成部分。有许多不同的方法和策略来处理身份验证。任何项目所采用的方法都取决于其特定的...

  • 前端鉴权和缓存相关收藏

    Auth2鉴权 JWT cookie-session 缓存相关

  • JWT鉴权 Session鉴权

    JWT鉴权:image.png session鉴权:image.png

  • 从零搭建个人博客(四)-集成jwt

    为什么使用 JWT 集成 jwt 生成 token 编写 Auth 中间件 在接口中进行拦截 使用 koa-jwt...

  • Koa 使用 JWT 实现鉴权

    JWT 鉴权的优势 JWT (JSON Web Token) 是现今比较主流的的登录鉴权方式。token 类似一个...

  • 记springmvc集成jwt鉴权

    jwt鉴权就不多说了,自行百度...网上关于springmvc集成的jwt的文章不少,但是都稍显复杂了一点.. 1...

  • JWT 鉴权

    JWT 是什么 JSON Web Token(JWT)是一个开放式标准(RFC 7519),它定义了一种紧凑且自包...

  • JWT 鉴权

    使用 koa-jwt + jsonwebtoken 完成用户鉴权功能。项目地址:https://github.co...

  • jwt鉴权

    什么是JWT(Json web token): JWT是目前最流行的跨域认证解决方案。基于json的开放标准(R...

  • JWT

    概述 JWT 基于 token 的鉴权机制,基于 token 的鉴权机制类似于 http 协议也是无状态的,它不需...

网友评论

      本文标题:nest.js 集成 auth 鉴权 JWT

      本文链接:https://www.haomeiwen.com/subject/nmhqkdtx.html