ノードを学びましょう.JSとのバックエンドを構築する.レッスン4:ログインとユーザー認証


この記事はもともと1967年に出版されましたhttps://www.blog.duomly.com/node-js-course-building-fintech-banking-app-lesson-4-user-login-authentication/

ノードへのイントロ。ログインとユーザ認証


前の週に私たちはノードを始めました.私たちがFintechバンキングアプリケーションを構築しているJSコース.このコースでは巣を使います.フレームワークとタイプスクリプト.畝
first lesson of this Node.js Course , 我々はプロジェクトを開始し、データベースを設定する移行を行った.
second and third lesson , 私は、ユーザー登録に焦点を当て、データベースに新しいユーザーを作成し、エンドポイントから適切な応答を表示します.
今、ログインと認証のエンドポイントを作成する時間です.畝
あなたが同様のバックエンドを構築しようとするならば、あなたは我々を試みることができますGolang Course , 我々は、同じアプリケーションを作成しているところ.
このアプリケーションのフロントエンドを作成してくださいAngular 9 Course .
そして、読書のポストよりビデオチュートリアルから学ぶことを好む人々のために、私はあなたのためにビデオ版を持っています.
あなたが前のレッスンからコードを持っていないならば、我々から我々のGithub repo for lesson three .
お気に入りのコードエディターでコードを開きましょう!

更新ユーザー実体


最初に、2行のコードを追加しますuser.entity.ts ユーザとアカウントの接続を定義するファイル.ユーザーエンティティファイルを開き、下に次のコードを追加します.
@HasMany(() => Accounts, 'UserId')
public accounts: Accounts[];
また、アカウントエンティティがインポートされている場合は注意してくださいそうでない場合は、ファイルの先頭に置いてください.畝
そして今、ログイン機能を作成することができます.

2 .ログイン機能を作成する


開けましょうuser.service.ts ファイルは、我々のレジスタ機能の小さな変更を行うことによって開始されます.JWT内のコードを変更します.signal ()関数.
const jwtToken = jwt.sign({id: user.id, username: user.Username, email: user.Email}, process.env.JWT_KEY, jwtConfig);
準備ができたら、ログインロジックを構築することから始めることができます.下にregister() 関数は新しいlogin() 資格情報を持つ関数.初めに、データベース内のユーザーを、私たちの資格情報と同じユーザー名で見つけましょう.
public async login(credentials: any): Promise<object> {
    const user = await Users.findOne<Users>({
      where: { Username: credentials.Username },
      attributes: { exclude: ['createdAt', 'updatedAt'] }
    });
}
ユーザが定義されるべきであるとき、指定されたユーザ名を持つユーザが存在しないことが時々起こるかもしれないことを覚えておく価値があります.したがって、ユーザー定数のすぐ下に、ユーザーが定義されていない場合、エラーを返す条件文を作成します.
if (!user) {
  return {
    success: false,
    message: 'User does not exist.'
  }
}
偉大な、ユーザーの状況は、今はかなり明確ですので、パスワードについての方法を見つけることができます.ユーザーにログインするには、ログインフォームで渡されるパスワードがデータベース内の保存と同じかどうかチェックする必要があります.
データベースに保存する前に、パスワードの変更を行いました.そして、これらの2つの値を比較することができるようになりました.ログインフォームからパスワードを操作する必要があります.
const inputPassword = crypto.createHmac('sha256', credentials.Password + user.Salt.trim()).digest('hex');
const isPasswordCorrect = user.Password.trim() === inputPassword.trim();
両方のパスワードは、同じ形式で比較されたので、今では、ユーザーがログインできるかどうか明確です.パスワードが異なる場合には、別の条件文で処理しなければなりません.
if (!isPasswordCorrect) {
  return {
    success: false,
    message: 'Password is not correct.'
  }
}
の初めにlogin() 関数は、データベースからユーザーを持っていますが、ユーザーアカウントを取得できませんでした.そういうわけで、我々は口座を別々に得なければなりません.私たちはAccountTorServiceファイルで準備された任意の機能を持っていないなぜ私たちは関数の既存の名前を使用するつもりです、そして、後で私たちはaccounts.service.ts ファイルと作成getAccountsByUserId() メソッド.アカウントとJWTトークンのコードを作成して、返信します.
const accounts = await this.accountsService.getAccountsByUserId(user.id);
const jwtToken = jwt.sign({ id: user.id, email: user.Email, username: user.Username }, process.env.JWT_KEY, jwtConfig);
そこで、最後にするのはレスポンスオブジェクトを準備し、それを返すことです.
const response = {
  user: {
    id: user.id,
    username: user.Username.trim(),
    email: user.Email.trim(),
    accounts,
  },
  token: jwtToken,
  success: true,
}

return response;
すごい、我々のログイン機能は、準備ができています.私たちはすぐにユーザーのアカウントを取得するために不足している機能を作成する必要があることを忘れないでください.
また、全体のコードを見てくださいlogin() 関数は、同じように見えるようにします.
public async login(credentials: any): Promise<object> {
  const user = await Users.findOne<Users>({
    where: { Username: credentials.Username },
    attributes: { exclude: ['createdAt', 'updatedAt'] }
  });

  if (!user) {
    return {
      success: false,
      message: 'User does not exist.'
    }
  }

  const inputPassword = crypto.createHmac('sha256', credentials.Password + user.Salt.trim()).digest('hex');
  const isPasswordCorrect = user.Password.trim() === inputPassword.trim();

  if (!isPasswordCorrect) {
    return {
      success: false,
      message: 'Password is not correct.'
    }
  }

  const accounts = await this.accountsService.getAccountsByUserId(user.id);
  const jwtToken = jwt.sign({ id: user.id, email: user.Email, username: user.Username }, process.env.JWT_KEY, jwtConfig);
  const response = {
    user: {
      id: user.id,
      username: user.Username.trim(),
      email: user.Email.trim(),
      accounts,
    },
    token: jwtToken,
    success: true,
  }

  return response;
}

2 . getAccountsbyUserID関数を作成する


さあ行きましょうaccounts.serivce.ts ファイルを更新し、行方不明の関数を追加します.
public async getAccountsByUserId(UserId: number): Promise<object> {
  const accounts = await Accounts.findAll<Accounts>({
    where: { UserId },
    attributes: { exclude: ['createdAt', 'updatedAt'] }
  });

  return accounts ? accounts : [];
}
それが完了したら、確認することができますが、もはやユーザーサービスのエラーはありません.

3 .ログイン終了点を作成する


ユーザーコントローラを開き、ログインパスで実際のエンドポイントを作成する時です.POSTメソッドを使用します.
@Post('login')
public async login(@Res() res, @Body() credentials: any): Promise<any> {
  const result: any = await this.usersService.login(credentials);
  if (!result.success) {
    throw new HttpException(result.message, HttpStatus.BAD_REQUEST);
  }
  return res.status(HttpStatus.OK).json(result);
}
soo、我々は最初のテストを開始することができますが、最初にバックエンドを実行することを忘れないでくださいnpm start .

テストログイン


お使いのアプリケーションが稼働している場合は、APIをテストできるように任意のツールを開きます.私はこの目的のために郵便配達人を使っています.マイノード.JSのアプリはhttp://localhost:3000 , そして、私は、私がテストすることができる既存のユーザーを持っています.
任意のユーザーを持っていない場合は、登録エンドポイントを使用して1つを作成してください.下に、あなたは私の結果を見ることができます.

すべてがうまくいけば、我々のコードに戻って、トークンをチェックした後にユーザーデータを与える認証機能を作成しましょう.

認証機能の作成


この手順では、JWTトークンとユーザーIDを取得するユーザーサービス内の別の関数を作成します.次に、トークンが有効かどうかを確認し、それに基づいてユーザーデータを返します.
の定義から始めましょうauthenticate() 関数はlogin ()関数のすぐ下にあり、関数の内部では、ユーザをアカウントで取得することから始めましょう.
public async authenticate(id: number, token: string): Promise<any> {
  const user = await Users.findOne<Users>({
    where: { id },
    include: [
      {
        model: Accounts,
        where: { UserId: id },
        required: true,
      }
    ],
  });
}
準備ができたら、トークンを使用して確認することができますjwt.verify() トークン、ユーザーID、ユーザ名、およびユーザーメールをコード化するために使用されるデータを返す関数.次に、トークンからのIDを、関数に渡されたIDと比較します.
const decodedToken = jwt.verify(token, process.env.JWT_KEY, jwtConfig);
const isTokenValid = decodedToken.id === Number(id);
IDSの比較が準備ができているならば、我々はユーザーデータが通過されることができるかどうかわかっています.彼らが通過できないならば、我々は間違っていることを返す条件文を作成する必要があります.
if (!isTokenValid) {
  return {
    success: false,
    message: 'User is not authorized.'
  }
};
これが完了したら、最後に行うのはレスポンスオブジェクトを作成して返すことです.それで、それをしましょう.
const response = {
  user: {
    id: user.id,
    email: user.Email.trim(),
    username: user.Username.trim(),
    accounts: user.accounts,
  },
  token,
  success: true,
}

return response;
そして最後に、全体のコードを見てくださいauthenticate() 機能は何も行方不明であることを確認します.
public async authenticate(id: number, token: string): Promise<any> {
    const user = await Users.findOne<Users>({
      where: { id },
      include: [
        {
          model: Accounts,
          where: { UserId: id },
          required: true,
        }
      ],
    });

    const decodedToken = jwt.verify(token, process.env.JWT_KEY, jwtConfig);
    const isTokenValid = decodedToken.id === Number(id);
    if (!isTokenValid) {
      return {
        success: false,
        message: 'User is not authorized.'
      }
    };

    const response = {
      user: {
        id: user.id,
        email: user.Email.trim(),
        username: user.Username.trim(),
        accounts: user.accounts,
      },
      token,
      success: true,
    }

    return response;
  }

6認証終了点を作成する


前述のように、我々はエンドポイントを作成しようとしています.エンドポイントはID変数からなります.
ここではPOSTメソッドも使用しますが、さらに@Headers() and @Param() はい.ファイルの先頭にインポートしてください.
ヘッダは認証ヘッダーを得るのに便利です.
@Post(':id')
  public async authenticate(@Param() params, @Res() res, @Headers() headers): Promise<any> {
    const token = headers.authorization.replace('Bearer ', '');
    const result: any = await this.usersService.authenticate(params.id, token);
    if (!result.success) {
      throw new HttpException(result.message, HttpStatus.BAD_REQUEST);
    }
    return res.status(HttpStatus.OK).json(result);
  }
すごい、我々はそれをテストする準備ができているようだ!

テスト認証


ここで、アプリケーションを再起動し、APIテスト用のツールを開きます.
この場合に必要な最初のものはトークンです.なぜなら、エンドポイントをテストするためにパスを渡さなければならないからです.
認証ヘッダーを設定しながら、トークンの前に“ベアラー”を追加することを忘れないでください.

結論


おめでとう!あなただけのノードを使用してユーザーのログインと認証を作成しました.JSとネスト.フレームワーク.
いくつかのコードを逃した場合は、見てくださいGithub .
このノードを見つけてください.JSコース.



読んでくれてありがとう.
からのアナ