iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 24
0
Modern Web

讀官網文件邊走邊學nest.js系列 第 24

Day24 Http Bearer Token保護API End Point-使用Passport

雖然TypORM還有transaction及migration可以繼續try,但繼續這樣下去就快要變成TypeORM鐵人30天....

還是拉回來nestjs framework本身,接下來到鐵人賽結束打算讀

  • Passport
    • node.js裡面有名的使用者驗證套件
  • CQRS
    • 是一種Design Pattern,分離service layer跟repository layer,適合中大型應用程式
  • Config Service
    • nestjs管理常數的Service,搭配dotenv套件,把如DB相關資訊存成.env檔,不同環境(dev/prod)使用不同的設定檔

今天就先來分享Passport,他提供基本的bearer、jwt及oauth第三方登入,只要安裝不同的Strategy,官網上Strategies頁面也可以搜尋

如:

在開始之前,我把到目前的code做一些refactor,這樣code比較乾淨一點

  • 建立user module把關於User、Role及Department的對應到的Http handler從app.controller抽出來
  • UsersService、RolesService及DepartmentService也一併移到

做refactor過程中十分流暢,大部分都是從app.controller copy&paste,除了import路徑修正外,幾乎不用做什麼變更

建立module建議用@nest/cli會建立資料夾、檔案及自動import AppModule

nest g module moduleName


流程大致如下:

  1. 建立AuthModule
    • 用來統一存放關於Auth的component
  2. 建立AuthService
    • 寫驗證使用者的邏輯、issue token等
  3. 新增UserService驗證方法
  4. 建立HttpStrategy
    • 告訴Passort Module怎麼驗證使用者,通常是呼叫auth service裡面的方法
  5. 在AuthModule下的provider註冊AuthService及HttpStrategy
  6. import UserModule
    • 需要存取使用者資訊,驗證帳號密碼等
  7. 到要保護的Controller或handler method套上@UseGuards()
  8. 測試

先用已知bearer token來理解Passport的運作,

安裝相關套件

yarn add @nestjs/passport passport passport-http-bearer

新增AuthModule

nest g mo auth

新增AuthService

在auth資料夾下新增auth.service.ts

@Injectable()
export class AuthService {
  constructor(
      // 注入機UsersService,所以需要import UsersModule
      // 底下的provider才能被注入
      private readonly usersService: UsersService,
  ) {}

  async validateUser(token: string): Promise<any> {
    // 先假定token已知,由userService回傳使用者資料
    // 如果token不正確則回傳null
    return await this.usersService.findOneByToken(token);
  }
}

新增使用者驗證方法

修改user.service.ts

...
async findOneByToken(token){
    // 假定token為ironNest
    if (token === 'ironNest')
      return this.getUserById(10);
    else
      return null;
}
...

新增HttpStrategy

到auth資料夾下新增passport資料夾

在passport資料夾新增bearer資料夾及jwt資料夾(明天要用)

新增http.strategy.ts

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

import { AuthService } from 'auth/auth.service';
import {PassportStrategy} from '@nestjs/passport';
import {Strategy} from 'passport-http-bearer';

@Injectable()
// 要繼承@nest/passport下的PassportStrategy並傳入passport
// 本日是以http bearer為例
export class HttpStrategy extends PassportStrategy(Strategy) {

    constructor(private readonly authService: AuthService){
        super();
    }
    async validate(token: string){
        const user = await this.authService.validateUser(token);
        if( !user){ // 如果用token找不到使用者,就丟unauthorized exception
            return new UnauthorizedException();
        }
        return user; // 有找到使用者,passport會把user物件存在req中
    }
}

在AuthModule註冊provider

auth.module.ts

@Module({
    imports: [
        UserModule,
    ],
    providers: [
        AuthService,
        HttpStrategy,
    ],
})
export class AuthModule {}

到User controller套用@UseGuards()

修改users.controller.ts

import { AuthGuard } from '@nestjs/passport';

// UseGuards()傳入@nest/passport下的AuthGuard,並指定用bearer驗證
@UseGuards(AuthGuard('bearer'))
@Controller('users')
export class UsersController {
...
}

使用postman測試

顯示401 Unauthoized

在authorization指定bearer token

可以顯示users資訊

明天來討論JWT

Github source code


上一篇
Day23-'TypeORM(十) Embedded Entity及Entity Listener& Subscriber
下一篇
Day25 JSON Web Token(JWT)保護API Endpoint-使用Passport
系列文
讀官網文件邊走邊學nest.js31

尚未有邦友留言

立即登入留言