aurora-article icon indicating copy to clipboard operation
aurora-article copied to clipboard

nestjs 用户授权

Open starryiu opened this issue 2 years ago • 0 comments

使用 nestjs 完成用户注册、登录和授权的步骤流程,包括使用 bcrypt 密码加密,jwt加密生成 token,以及如何验证 token 使用中间件。

完成注册接口

在注册时使用bcrypt对密码进行加密。

private saltRounds = 10;
private myHash = (password: string): Promise<string> => {
  return new Promise((resolve) => {
    bcrypt.hash(password, this.saltRounds, function (err, hash) {
      if (!err) resolve(hash);
    });
  });
};

async create(createUserDto: CreateUserDto) {
  //防止用户名重复
  const user = await this.userModel.findOne({
    username: createUserDto.username,
  });
  if (user) {
    return {
      statusCode: 403,
      message: '用户名已存在',
    };
  }
  const hash = (await this.myHash(createUserDto.password)) as string;
  createUserDto.password = hash;
  return this.userModel.create(createUserDto);
}

完成登录接口

登录先查询用户名是否存在,然后bcrypt对比密码,密码正确之后使用jsonwebtoken生成登录token,token中存入用户的id即可。

其中 privateKey 单独保存到一个文件里。

前端登录后需要把token存到local中,需要用户授权时需要token中的id。

private myCompare = (password: string, hash: string): Promise<boolean> => {
  return new Promise((resolve) => {
    bcrypt.compare(password, hash, function (err, result) {
      if (!err) resolve(result);
    });
  });
};

async login(loginUserDto: LoginUserDto) {
  const user = await this.userModel.findOne({
    username: loginUserDto.username,
  });
  if (!user) {
    return {
      statusCode: 403,
      message: '用户名不存在',
    };
  }
  //查看密码正确
  const passwordIsTrue = await this.myCompare(
    loginUserDto.password,
    user.password,
  );
  if (!passwordIsTrue) {
    return {
      statsuCode: 403,
      message: '密码输入错误',
    };
  }
  //生成token
  const token = 'Bearer ' + jwt.sign({ id: user._id }, privateKey);
  return {
    statusCode: 200,
    message: {
      user,
      token,
    },
  };
}

验证 token

需要用户授权的接口都需要验证token,将授权部分写成中间件。

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  constructor(@InjectModel('User') private userModel: Model<UserDocument>) {}

  private jwtVerify = (authorization: string) => {
    return new Promise((resolve) => {
      jwt.verify(authorization, privateKey, (err, res) => {
        if (!err) resolve(res);
        else resolve(null);
      });
    });
  };

  async use(req: any, res: any, next: () => void) {
    const authorization = req.headers?.authorization;
    if (!authorization) {
      throw new HttpException(
        '未传入authorization字段的headers',
        HttpStatus.FORBIDDEN,
      );
    }
    const tokenInfo: any = await this.jwtVerify(authorization.split(' ').pop());
    if (!tokenInfo?.id) {
      throw new HttpException('token未授权', HttpStatus.FORBIDDEN);
    }
    const user = await this.userModel.findOne({ _id: tokenInfo.id });
    if (!user) {
      throw new HttpException('未授权的用户', HttpStatus.FORBIDDEN);
    }
    req.user = user;
    next();
  }
}

starryiu avatar Feb 03 '23 09:02 starryiu