본문 바로가기

ChatGPT/AWS Serverless

[AWS][LAMBDA][AUTHORIZER] TOKEN-BASED와 REQUEST-BASED 구현하기 - 3

반응형

 

https://tobelinuxer.tistory.com/55

 

AWS LAMBDA를 사용한 SERVERLESS API 인증 및 권한 부여: TOKEN-BASED와 REQUEST-BASED AUTHORIZER 구현하기 - 2

https://tobelinuxer.tistory.com/54 AWS Lambda를 사용한 Serverless API 인증 및 권한 부여: Token-based와 Request-based Authorizer 구현하기 - 1 서버리스 아키텍처에서 API 인증 및 권한 부여는 매우 중요한 부분입니다. A

tobelinuxer.tistory.com

이전 포스트에 이어서

이 페이지에서는 5.테스트 6. 결과에 대해 다루도록 하겠습니다.


 

1. 개요

  • Serverless API와 인증 및 권한 부여의 중요성

2. API Gateway Authorizer 소개

  • Token-based Authorizer
  • Request-based Authorizer

3. Token-based Authorizer

  • Token-based Authorizer 작동 원리
  • Token-based Authorizer 구현 예시

4. Request-based Authorizer

  • Request-based Authorizer 작동 원리
  • Request-based Authorizer 구현 예시

5. 테스트

6. 결론


 

5. 테스트

 

5.1 코드 반영

 

다음 코드들은 기존 코드를 바탕으로 수정된 코드들입니다.

 

먼제 handler.ts입니다.

 

// import serverless from 'serverless-http';
import serverlessExpress from '@vendia/serverless-express';
import { APIGatewayProxyEvent, Context, Callback } from "aws-lambda";

import { Express } from "express";

import app from './app';

let serverlessExpressInstance: any = null;

function adaptor(event: APIGatewayProxyEvent, context: Context) {
    const headers = event.multiValueHeaders || {}; // NOTE: Mutating event.headers; prefer deep clone of event.headers
    const eventWithoutBody: { body?: string | null } = Object.assign({}, event);
    if (eventWithoutBody.body) {
        delete eventWithoutBody.body;
    }

    headers['x-apigateway-event'] = [encodeURIComponent(JSON.stringify(eventWithoutBody))];
    headers['x-apigateway-context'] = [encodeURIComponent(JSON.stringify(context))];
}

function setup(event: APIGatewayProxyEvent, context: Context, callback: Callback, app: Express) {
    serverlessExpressInstance = serverlessExpress({ app: app });
    return serverlessExpressInstance(event, context, callback)
}

export let app_handler = (event: APIGatewayProxyEvent, context: Context, callback: Callback) => {

    adaptor(event, context);

    if (serverlessExpressInstance) {
        return serverlessExpressInstance(event, context, callback);
    }

    return setup(event, context, callback, app);
}

 

기존 serverless-express 라이브러리 대신 @vendia/serverless-express 라이브러리를 사용하였습니다.

 

serverless-express 라이브러리은 더 이상 지원하지 않고 모든 기능을 @vendia로 넘겼다고 합니다.

 

이에 따라 api gateway의 request id를 express로 넘기기 위해서 adaotor 함수를 추가하여 event의 headers에 x-apigateway-event와 x-apigateway-context에 APIGateway Event 와 context 내용을 삽입했습니다.


 

다음은 app.ts 입니다.

 

import express, { NextFunction, Request, Response } from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import cookieParser from 'cookie-parser';
import compression from 'compression';

export interface Options {
  reqPropKey?: string;
  deleteHeaders?: boolean;
}

export let eventContext = (options?: Options) => (req: Request & { [name: string]: any }, _res: Response, next: NextFunction) => {
  options = options || {}; // defaults: {reqPropKey: 'apiGateway', deleteHeaders: true}
  const reqPropKey = options.reqPropKey || 'apiGateway';
  const deleteHeaders = options.deleteHeaders === undefined ? true : options.deleteHeaders;

  if (!req.headers['x-apigateway-event'] || !req.headers['x-apigateway-context']) {
      console.error('Missing x-apigateway-event or x-apigateway-context header(s)');
      next();
      return;
  }

  if (reqPropKey != null) {
      req[reqPropKey] = {
          event: JSON.parse(decodeURIComponent(req.headers['x-apigateway-event'] as string)),
          context: JSON.parse(decodeURIComponent(req.headers['x-apigateway-context'] as string))
      }
  }

  req.headers['x-apigateway-event-requestId'] = req.apiGateway?.event.requestContext.requestId;

  if (deleteHeaders) {
      delete req.headers['x-apigateway-event']
      delete req.headers['x-apigateway-context']
  }

  next();
}


const app = express();

//API Gateway event parsing
app.use(eventContext());

// Body parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// CORS
app.use(cors());

// Security
app.use(helmet());

// Logging
app.use(morgan((tokens, req: any, res) => {
  const requestId = req.headers['x-apigateway-event-requestId']; // API Gateway 요청 ID 가져오기

  return JSON.stringify({
    request_id:requestId,
    method: tokens.method(req, res),
    url: tokens.url(req, res),
    status: tokens.status(req, res),
    response_time: tokens['response-time'](req, res) + ' ms',
    remote_addr: tokens['remote-addr'](req, res),

  });
}));

// Cookie parser
app.use(cookieParser());

// Compression
app.use(compression());

// 라우터와 기타 애플리케이션 로직은 여기에 추가합니다.

app.get('/hello', (req: Request, res: Response) => {
  console.log(req.query);

  res.send('Hello from Serverless TypeScript Express app!!!!!');
});

app.post('/hello', (req: Request, res: Response) => {
  console.log(req.body);

  res.send('Hello from Serverless TypeScript Express app!!!');
});

app.delete('/hello', (req: Request, res: Response) => {
  console.log(req.body);

  res.send('Hello from Serverless TypeScript Express app!!!');
});

export default app;

 

handler에서 추가 생성된 x-apigateway-event 및 x-apigateway-context를 파싱해서 request_id를 추출하는 eventContext 미들웨어 함수를 추가하였습니다.

 

이를 이용하여 morgan에서 request_id를 출력하도록 수정하였습니다.

 


 

마지막으로 serverless.yml 입니다.

service: my-service

plugins:
  - serverless-esbuild
  - serverless-offline

custom:
  serverless-offline:
    httpPort: 3000

provider:
  name: aws
  runtime: nodejs16.x
  lambdaHashingVersion: 20201221
  memorySize: 128
  timeout: 5
  stage: dev
  region: ap-northeast-2

functions:
  tokenAuthorizer:
    handler: src/auth/tokenAuthorizer.handler
  requestAuthorizer:
    handler: src/auth/requestAuthorizer.handler    
  app:
    handler: src/handler.app_handler
    events:
      - http:
          path: /app/{proxy+}
          method: ANY
          cors: true
  appToken:
    handler: src/handler.app_handler
    events:
      - http:
          path: /app-token/{proxy+}
          method: ANY
          cors: true
          authorizer:
            type: TOKEN
            name: tokenAuthorizer
            identitySource: method.request.header.Authorization                
  appRequest:
    handler: src/handler.app_handler
    events:
      - http:
          path: /app-request/{proxy+}
          method: ANY
          cors: true
          authorizer:
            type: REQUEST
            name: requestAuthorizer
            identitySource:
              - method.request.header.CustomHeader

 

이전 글에서 다루었던 내용을 포함하여 수정하였습니다.

 

이에 따라 전체 파일 구조는 아래와 같습니다.

 


5.2 오프라인 테스트 ( token )

 

Serverless Framework의 serverless-offline 플러그인을 사용하면, 로컬에서 API Gateway와 Lambda 함수를 에뮬레이션하여 오프라인 테스트를 진행할 수 있습니다.

 

이를 통해 개발 과정에서 빠르게 피드백을 받을 수 있으며, 클라우드 환경으로 배포하기 전에 로컬에서 먼저 테스트를 해 볼 수 있습니다.

 

/app/hello 실행화면

 

기존 path ( Authorizer 없는 ) 정상 동작 함을 위 결과에서 확인할 수 있습니다.

 

/app-token/hello( Authorization Header 없이) 실행화면

 

Authorization Header 미 포함시 401 오류를 내보내는 것을 확인할 수 있습니다.

 

/app-token/hello( 잘못된 토큰 ) 실행화면

 

Authorization Header에 잘못된 토큰이 입력되었을 시 403 오류를 내보내는 것을 확인할 수 있습니다.

 

/app-token/hello( 정상 토큰 ) 실행화면

 

Authorization Header에 정상 토큰이 입력되었을 시 정상 화면을 출력하는 것을 확인할 수 있습니다.


 

5.3 베포 및 실제 서비스 환경에서의 테스트 ( request )

 

serverless framework를 이용한 베포를 진행하였습니다.

 

sls deploy

 

성공적으로 베포가 완려된 모습을 아래와 같이 확인할 수 있습니다.

 

 

아래 결과들은 각각 정상 토큰, 잘못된 토큰, CustomHeader 미 포함에 대한 결과 화면들입니다.

 

/app-request/hello( 정상 토큰 ) 실행화면

 

/app-request/hello( 잘못 토큰 ) 실행화면
/app-request/hello( CustomHeader 없이) 실행화면

 

6. 결론

 

이 글에서는 Serverless Framework와 AWS Lambda를 사용하여 Authorizer를 구현하고 배포하는 방법에 대해 알아보았습니다.

 

각 유형의 Authorizer에 대한 작동 원리와 설정 방법을 살펴보았으며, 로컬에서 오프라인 테스트를 진행하는 방법을 다루었습니다.

 

마지막으로 서비스를 AWS에 배포하고, 실제 환경에서 서비스가 올바르게 작동하는지 테스트하는 방법을 소개했습니다.

이를 통해 서비스의 보안성을 높이고, 인증 및 권한 관리를 쉽게 구현할 수 있습니다.

 

추가로, 서비스의 요구사항에 따라 다양한 Authorizer 유형을 사용하여 개발자들이 원하는 인증 방식을 구현할 수 있게 됩니다.

 

Serverless Framework와 AWS Lambda를 사용하여 Authorizer를 성공적으로 구현하고 배포한 후, 서비스의 안정성과 확장성을 높이기 위해 다양한 미들웨어와 기타 AWS 서비스를 활용하여 최적화할 수 있습니다.

 

이를 통해 서비스의 품질을 높이고, 사용자들에게 더 나은 경험을 제공할 수 있게 됩니다.

 

이제 AWS Lambda와 Serverless Framework를 사용하여 Authorizer를 구현하고 배포하는 데 필요한 모든 지식을 갖추게 되었습니다.

 

이를 기반으로 다양한 인증 및 권한 관리 요구사항에 맞게 서비스를 개발하고 확장해 나가실 수 있습니다.

 

앞으로도 지속적인 학습과 실습을 통해 Serverless 애플리케이션 개발에 대한 전문성을 더욱 향상시키고, 클라우드 기반 서비스의 다양한 기능과 최적화 방법을 계속 탐구하면서 개발자로서의 역량을 강화해 나가시길 바랍니다.

 

 

지금 보시는 글은 ChatGPT의 도움을 받아 작성되었습니다.

반응형