TOPIC/Code

#01 REST API - 개념과 보안

admin_cloud 2024. 4. 23. 15:54

안녕하세요. TAK 입니다:)

 

이번 포스팅은 관련 업무를 하면서 REST API 에 대해 정리할 필요성을 느꼈기에 작성하게 되었습니다😶

 

아래처럼 2편으로 나눠서 포스팅할 예정입니다!

#01 REST API - 개념과 보안

#02 REST API - 활용

 

엄청난 내용을 다루진 않지만, 그래도 From ? To ! 가 될 수 있는 내용이 될 수 있지 않을까 하는 생각과 함께

 

시작합니다!

 


Contents

    1. API 란?

    1-1. 정의

    1-1-1. 개념

    : API는 Application Programming Interface의 약자로, 소프트웨어 응용 프로그램들이 서로 통신할 수 있도록 만들어진 인터페이스를 말합니다. 

    이를 쉽게 표현하자면, "애플리케이션 간 상호 작용할 수 있는 중간 다리 역할을 한다"라고 이해하시면 좋을 것 같네요.
    (아래 출처에서 가져온 이미지로는 "중간 다리 역할 = 점원" 으로 표현하고 있네요😄)

    이러한 역할을 하는 API는 서로 통신할 수 있는 방법을 제공하며, 데이터 및 서비스에 접근하고 사용할 수 있게 하는 매개체입니다.

    출처 : https://blog.wishket.com/wp-content/uploads/2019/10/API-%EC%89%BD%EA%B2%8C-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0.png

     

    1-1-2. API  유형

    Private APIs Partner APIs Open(Public or External) APIs 
    말 그대로 Prviate API는 내부에서 사용되는 API입니다.
    기업 내부에서 개발자나 사용이 허용되는 일부 사용자가 기업의 IT 시스템이나 애플리케이션을 통합, 구축 등의 목적으로 사용됩니다.
    즉, 외부(제3자)에게는 노출되지 않습니다.
    Partner API의 경우, 기업 간 계약을 통해 허용된 비즈니스 파트너가 사용가능한 API 입니다.
    공통으로 앱을 개발하거나, 모니터링 및 성능 개선의 목적으로 타사의 솔루션을 사용할 때, 제공되는 API라고 할 수 있습니다.
    Open API는 무료(개방형)와 상용 2가지 유형이 있습니다.

    무료(개방형)의 경우, 대부분의 기능이 공개되어 있으며, 별도의 제약 및 승인 없이 공개된 API를 사용하여 애플리케이션을 생성하고 테스트할 수 있습니다.

    상용 API의 경우, 구독료 혹은 사용한 만큼 지불하는 Pay As You Go 방식을 통해 제공합니다. 

     

    1-1-3. API 방식

    위 그림에서 보이는 것과 같이, 프로그램 - 프로그램 관계를 클라이언트 - 서버 관계로 아래의 그림처럼 표현할 수 있습니다.

     

    예를 들어, 사용자가 웹 브라우저에서 네이버에 접속했을 때

    네이버는 다양한 카테고리를 통해 정보를 확인하고 조작할 수 있도록 구성을 해두었습니다.

    네이버의 뉴스 카테코리를 클릭했을 때, 뉴스라는 API를 호출(Request)하고 해당 요청에 대해 API라는 요청을 처리하는 규칙을 통해서 Web Server는 요청을 해석하고 데이터가 저장된 Database에 여러 섹션(정치, 문화, 스포츠 등) 다양한 데이터(정보)를 가져와 사용자에서 보여주는 응답(Reposne)하는 과정이라고 할 수 있습니다.

    출처 : https://www.altexsoft.com/media/2019/06/1.png

     

    1-2. REST API 란?

    1-2-1. REST API 개념 및 특징

    : 그렇다면 REST API는 무엇일까요? 앞서 설명한 API는 여러 형태가 있을 수 있습니다.

    가장 흔한 형태는 웹(HTTP) API로, HTTP 프로토콜을 사용하며 네트워크를 통해 상호 작용 하도록 설계되어 있습니다.

     

    일반적으로 테스트 형식의 코드로 작성하면서 만드는 API가 웹 API에 해당합니다.

     

    이는 각 사용자들이 자기만의 API를 만드는 것은 다른 사람과 공통으로 적용할 수 없는 자기만의 규칙을 만든 다는 것으로 볼 수 있습니다. 즉, 기업에서 담당자가 변경되거나, 다른 파트너와의 API를 공유할 때 타인이 이해하기 어렵다는 문제점이 발생할 수 있습니다.

     

    여기서에서 이러한 문제점을 개선하고자 그리고 API를 보다 잘 활용해 보고자 "REST(Representational State Transfer) API"가 등장하게 됩니다.

     

    REST(Representational State Transfer) API 는 네트워크 아키텍처의 한 형태로, 자원을 URI로 표현하고 해당 자원에 대한 행위를 HTTP METHOD(GET, POST, PUT, DELETE 등)를 통해 수행합니다.

     

    즉, 블로그 서비를 REST API로 구현하고자 한다면 아래와 같이 표현할 수 있습니다.

     

    1. 자원을 URL로 표현

    • 블로그의 모든 글: GET /posts
    • 특정 글: GET /posts/{post_id}
    • 새로운 글 생성: POST /posts
    • 글 업데이트: PUT /posts/{post_id}
    • 글 삭제: DELETE /posts/{post_id}

    2. 자원의 행위는 HTTP METHOD로 표현

    • GET: 자원을 읽기 위해 사용됩니다.
    • POST: 새로운 자원을 생성하기 위해 사용됩니다.
    • PUT: 자원을 업데이트하기 위해 사용됩니다.
    • DELETE: 자원을 삭제하기 위해 사용됩니다.

    3. 예시

    • 블로그 API를 사용하여 특정 ID를 가진 글을 읽고자 한다면
      GET /posts/123
    • 새로운 글을 작성하려면
      POST /posts

    이처럼 Represent 라는 의미에 맞게, 요청하는 내용만 보고 무엇을 요청하는 것인지 직관적으로 이해할 수 있어야 합니다.

     

    위의 표현은 METHOD에 충실히 표현한 것이므로, HTTP 프로토콜을 사용하기에 정확히 표현하자면 아래처럼 표현할 수 있습니다.

    *예시
    GET http(s)://with-cloud.tistory.com/posts/123

     

    또한, REST API는 크게 4가지로 이루어져 있습니다. ①권한 인증 등에 활용되는 header, ②위치를 나타내는 path, ③쿼리문을 활용한 query string, ④requsest 요청에 포함되는 body로 이루어져 있습니다. 일반적으로 ③query string은 GET 방식 ④body는 POST 방식에 활용됩니다.

     

    앞으로 진행할 보안 인증(Authorization) 테스트에서 인증을 위한 값들은 Header의 매개변수로써 사용되게 됩니다.

     

    1-2-2. REST API 설계 규칙

    • URI는 명사를 사용한다.(리소스명은 동사가 아닌 명사를 사용해야한다.)
    • 슬래시(/)로 계층 관계를 표현한다.
    • URI 마지막 문자로 슬래시(/)를 포함하지 않는다.
    • 밑줄(_)을 사용하지 않고, 하이픈(-)을 사용한다.
    • URI는 소문자로만 구성한다.
    • HTTP 응답 상태 코드 사용 - 클라이언트는 해당 요청에 대한 실패, 처리 완료 또는 잘못된 요청 등에 대한 피드백을 받아야 한다.
    • 파일 확장자는 URI에 포함하지 않는다.

     

    2. API 보안 방식

    : 그렇다면, 이러한 REST API는 어떻게 보호해야 하는 걸까요?

    대부분의 서비스의 구현은 API를 기반으로 통신합니다. 앞서 설명한 것처럼 클라이언트 - 서버 간 대부분의 통신이 API 방식으로 이뤄지게 되는데, 이는 데이터 및 시스템을 보호하고 여러 위협으로 보호하기 위해 필수적인 영역입니다.

    • 인증 및 권한 부여: 사용자가 인증되지 않은 경우 API에 액세스 할 수 없도록 보장해야 합니다. 또한, 권한이 없는 사용자가 보호되는 자원에 액세스하지 못하도록 권한을 제어해야 합니다.
    • 데이터 무결성: API를 통해 전송되는 데이터는 무결성을 유지해야 합니다. 즉, 데이터가 중간에서 변경되거나 손상되는 것을 방지해야 합니다.
    • 기밀성: 민감한 정보가 포함된 데이터는 인가된 사용자만 액세스할 수 있어야 합니다. 이러한 정보가 노출되면 비즈니스나 개인에게 심각한 피해를 줄 수 있습니다.
    • DDoS 및 악의적인 공격 방지: REST API는 인터넷을 통해 공개되므로 DDoS(분산 서비스 거부) 및 다양한 악의적인 공격에 취약합니다. 적절한 방어 메커니즘이 필요합니다.
    • 데이터 검증 및 처리: API에 입력되는 데이터는 검증되어야 합니다. 이는 유효성 검사를 통해 잘못된 요청이나 악의적인 데이터를 방지하는 것을 의미합니다.
    • 로그 및 감사: API에 대한 액세스 및 활동에 대한 로그를 유지하고 감사해야 합니다. 이를 통해 보안 사고가 발생했을 때 추적하고 대응할 수 있습니다.

    이러한 REST API의 보안을 위한 중요한 메커니즘인 인증(Authentication)과 인가(Authorization) 개념에 대해서 살펴보겠습니다.

     

    2-1. Authentication(인증)

    : 인증이란? API의 보호된 리소스에 대한 액세스를 허용하기 위해 확인하는 절차로 사용자가 자신이 주장하는 신원(예: 사용자 이름과 비밀번호)을 확인하는 과정입니다.

    • 사용자가 시스템에 로그인할 때 인증이 이루어집니다.
    • 대부분의 경우, 인증은 사용자 이름과 비밀번호를 사용하여 이루어지지만, 보다 안전한 방법으로는 토큰 기반 인증(JSON Web Token 등)이나 OAuth와 같은 프로토콜을 사용할 수 있습니다.
    • 인증이 성공하면 사용자는 시스템에 대한 액세스 권한을 부여받습니다.

     

    2-2. Authorization(인가)

    : 인가란? 인증된 사용자가 특정 자원이나 기능에 액세스 할 수 있는 권한을 가지고 있는지 확인하는 과정입니다.

    • 인가는 인증 이후에 이루어지며, 사용자의 역할이나 권한에 따라 자원에 대한 접근을 제어합니다.
    • 예를 들어, 사용자가 관리자로 인증되었지만 특정 리소스에 대한 관리자 권한이 없는 경우, 해당 리소스에 대한 액세스가 거부됩니다.
    • 인가는 일반적으로 역할 기반 접근 제어(Role-Based Access Control, RBAC)나 규칙 기반 접근 제어(Rule-Based Access Control)와 같은 방법을 사용하여 구현됩니다.

     

    이외에도, 네트워크 레벨 암호화, 메시지 무결성 보장, 메시지 본문 암호화 등이 있습니다.

     

    3. REST API 인증 방법

    : REST API인증 테스트를 위해, Python(Flask) 활용하였습니다.

     

    사용된 샘플 코드는 다음과 같습니다!

    from flask import Flask, request, jsonify
    
    app = Flask(__name__)
    
    # GET 요청을 처리하는 엔드포인트
    @app.route('/api/data', methods=['GET'])
    def get_data():
        # GET 요청의 쿼리 파라미터 가져오기
        param = request.args.get('param')
    
        if not param:
            return jsonify({'error': 'Parameter is missing'}), 400
    
        return jsonify({'message': f'GET 요청이 성공적으로 처리되었습니다. Parameter: {param}'})
    
    # POST 요청을 처리하는 엔드포인트
    @app.route('/api/data', methods=['POST'])
    def post_data():
        # POST 요청의 JSON 데이터 가져오기
        data = request.get_json()
    
        if not data:
            return jsonify({'error': 'JSON data is missing'}), 400
    
        return jsonify({'message': f'POST 요청이 성공적으로 처리되었습니다. Data: {data}'})
    
    if __name__ == '__main__':
        app.run(host="0.0.0.0 or Private IP", Port="5000" ,debug=True)

     

     

    우선 인증이 이뤄지지 않은 상태의 값을 확인해 보겠습니다.

    • (GET) 파라미터값 입력 X

     

    • (GET)  파라미터값 입력 O

     

    • (POST)
      • Request Content-Type 제약으로, 만약 body 에 대해 Json 형식으로 값을 전달하지 않으면 415 Unsupported Media Type 에러가 뜨게 됩니다.

     

     

    이처럼 외부에서 별도의 제약 없이 API를 호출하는 것은 보안 이슈와 데이터 보호에 취약합니다. 따라서 아래 4가지 인증 방법에 대해서 살펴보겠습니다.

     

    3-1. Basic Authentication

    출처 : https://voyager.postman.com/illustration/http-basic-auth-diagram-postman.svg

    • 가장 간단한 형태의 인증으로, 사용자 이름과 비밀번호를 인코딩(Base64)하여 HTTP 요청 헤더에 포함시키는 방식입니다.
      • 아래 샘플 코드의 내용처럼 "사용자이름:비밀번호 = admintak:pwdforapitest"라는 문자열을 인코딩
    • 보안적으로 취약할 수 있으므로 HTTPS(클라이언트 인증서, 신원 확인을 위한 검증)와 함께 사용하는 것이 좋습니다.

     

    *Basic Authentication TEST

    : 위 샘플코드를 기반으로 Basic Auth 를 구현해 보겠습니다.

    • 라이브러리 설치
    pip install Flask-BasicAuth

     

    • Basic Auth 적용 샘플 코드
      • 각 METHOD에 Basic Auth 데코레이터 추가
    from flask import Flask, request, jsonify
    from flask_basicauth import BasicAuth
    
    app = Flask(__name__)
    app.config['BASIC_AUTH_USERNAME'] = 'admintak'
    app.config['BASIC_AUTH_PASSWORD'] = 'pwdforapitest'
    
    basic_auth = BasicAuth(app)
    
    # GET 요청을 처리하는 엔드포인트
    @app.route('/api/data', methods=['GET'])
    @basic_auth.required
    def get_data():
        # GET 요청의 쿼리 파라미터 가져오기
        param = request.args.get('param')
    
        if not param:
            return jsonify({'error': 'Parameter is missing'}), 400
    
        return jsonify({'message': f'GET 요청이 성공적으로 처리되었습니다. Parameter: {param}'})
    
    # POST 요청을 처리하는 엔드포인트
    @app.route('/api/data', methods=['POST'])
    @basic_auth.required
    def post_data():
        # POST 요청의 JSON 데이터 가져오기
        data = request.get_json()
    
        if not data:
            return jsonify({'error': 'JSON data is missing'}), 400
    
        return jsonify({'message': f'POST 요청이 성공적으로 처리되었습니다. Data: {data}'})
    
    if __name__ == '__main__':
        app.run(host="0.0.0.0 or Private IP", Port="5000" ,debug=True)

     

    • Authorization(인증) 없이 호출할 경우, Status : 401 Unauthorized 

     

    • Authorization(인증) : Basic Auth 적용
      • 아래처럼 세팅하면, Postman에서 해당 값에 대해 인코딩(Base64)을 자동으로 지원해 주기 때문에 가능합니다.
      • 따라서, 실제로는 인코딩(or 디코딩) 사이트에서 값을 확인해야 합니다. 

     

    • 직접 인코딩 값을 HTTP 헤더 값으로 넣는 경우
    Authorization: Basic YWRtaW50YWs6cHdkZm9yYXBpdGVzdA==

     

    3-2. API Key Authentication

    출처 :  https://voyager.postman.com/illustration/api-key-auth-diagram-postman.svg

    • API 키 인증은 보통 정적인 문자열로 구성되며, 클라이언트가 서버에 요청을 보낼 때 이 키를 함께 전송합니다.
    • API 키는 주로 서비스나 애플리케이션을 식별하고 인증하는 데 사용됩니다. 이는 API를 사용하는 클라이언트가 서비스에 등록되어 있는지 여부를 확인하는 데에 주로 사용됩니다.
    • 말 그대로 API Key 를 사용하는 고유한 식별자이, 각 클라이언트에게 부여되어 API 요청에 포함됩니다.
    • API를 사용하고자 할 때, API Key를 발급받아 메시지 안에 넣어 호출합니다.
    • 클라이언트 간 동일한 API Key를 공유하기 때문에, 보안상 취약점이 존재하며 높은 보안 인증이 필요할 때는 적합하지 않습니다.

     

    *API Key Authentication TEST

    : 위 샘플코드를 기반으로 API Key Auth 를 구현해 보겠습니다.

    • API Key 생성
      • 일반적으로 API Key의 경우, "대소문자 + 숫자" 조합한 32글자로 이뤄져 있습니다.
    import secrets
    import string
    
    def generate_api_key(length=32):
        alphabet = string.ascii_uppercase + string.ascii_letters + string.digits
        api_key = ''.join(secrets.choice(alphabet) for i in range(length))
        return api_key
    
    # 32자 길이의 무작위 API 키 생성
    api_key = generate_api_key()
    
    print(api_key)

     

    • API Key Auth 적용 샘플 코드
      • 각 METHOD에 API Key Auth 데이레이터 추가
    from flask import Flask, request, jsonify
    from functools import wraps
    
    app = Flask(__name__)
    
    API_KEY = '[위 key 생성 스크립트에서 생성한 key 값 입력]'
    
    # API 키를 검증하는 함수
    def verify_api_key(api_key):
        return api_key == API_KEY
    
    # API 키 검증을 수행하는 데코레이터
    def require_api_key(view_function):
        @wraps(view_function)
        def decorated_function(*args, **kwargs):
            api_key = request.headers.get('X-API-KEY')
            if not api_key or not verify_api_key(api_key):
                return jsonify({'error': 'Unauthorized access'}), 401
            return view_function(*args, **kwargs)
        return decorated_function
    
    # GET 요청을 처리하는 엔드포인트
    @app.route('/api/data', methods=['GET'])
    @require_api_key
    def get_data():
        param = request.args.get('apitest')
        if not param:
            return jsonify({'error': 'Parameter is missing'}), 400
        return jsonify({'message': f'GET 요청이 성공적으로 처리되었습니다. Parameter: {param}'})
    
    # POST 요청을 처리하는 엔드포인트
    @app.route('/api/data', methods=['POST'])
    @require_api_key
    def post_data():
        data = request.get_json()
        if not data:
            return jsonify({'error': 'JSON data is missing'}), 400
        return jsonify({'message': f'POST 요청이 성공적으로 처리되었습니다. Data: {data}'})
    
    if __name__ == '__main__':
        app.run(host="0.0.0.0 or Private IP", Port="5000" ,debug=True)

     

    • Key 값 없이 호출할 경우, Status : 401 Unauthorized 

     

    • Basic Auth와 마찬가지로 Postman에서 지원하는 Authorization에서 인증 Type 설절하여 Key:Vaule 사용하여 인증
      • Key : 위 스크립트에서 "request.headers.get('X-API-KEY')" 을 확인해 보면 헤더 값에 Key Name을 X-API-KEY 사용하라고 표기되어 있으므로 해당 값 입력
      • Value : '[위 key 생성 스크립트에서 생성한 key 값 입력]' 해당 값 입력

     

    • Authorization(인증) : API Key 적용

    • 마찬가지로 Postman의 Authorization 사용 없이, Headers 값에 직접 Key:Vaule를 입력하여 인증할 수 있다.

     

    3-3. Token Authentication

    출처 : https://voyager.postman.com/illustration/jwt-auth-diagram-postman.svg

    • 사용자가 로그인할 때 서버에서 발급된 토큰을 클라이언트에게 제공하고, 클라이언트는 이 토큰을 이후의 요청에 함께 전송하여 인증합니다.
    • 즉, 로그인 정보(username, password 등)를 통해 사용자를 인증하고 발급된 API 토큰으로 사용자를 인증하는 방식입니다.
    • JWT(JSON Web Token) 방식이 일반적으로 많이 사용됩니다.
      • Json 객체를 사용하는 Key:Vaule 구조
    • Token 인증 과정을 설명하자면 다음과 같습니다.
      1. 사용자의 로그인(username, password) > POST /user/login with username:password
      2. 서브는 요청을 확인하고 Secret Key를 통해 Access Token 발급
      3. 사용자가 API를 통해 인증을 요청할 때, Header 값에 Authorization(인증):Acceess Token을 담아서 전달(요청)
      4.  서버는 JWT Signature를 확인하고, Playload로부터 사용자 정보를 확인하고 응답(자원을 Return)

     

    *Token(JWT) Authentication TEST

    : 위 샘플코드를 기반으로 Token(JWT) Auth 를 구현해 보겠습니다.

    • DB에 저장된 사용자가 로그인할 때 서버에서 발급된 토큰을 제공해야 하기 때문에 우선 DB와 사용자를 생성하겠습니다.
      • 테스트용이기 때문에 가볍고, 별도의 서버가 필요 없는 SQLite DB(Python 기본 라이브러리에 포함)를 사용하였습니다.
        import sqlite3
        
        # SQLite 데이터베이스 파일 경로
        DB_FILE = 'users.db'
        
        # 사용자 테이블 생성 함수
        def create_user_table():
            conn = sqlite3.connect(DB_FILE)
            cursor = conn.cursor()
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS users (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    username TEXT UNIQUE NOT NULL,
                    password TEXT NOT NULL
                )
            ''')
            conn.commit()
            conn.close()
        
        # 사용자 추가 함수
        def add_user(username, password):
            conn = sqlite3.connect(DB_FILE)
            cursor = conn.cursor()
            cursor.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, password))
            conn.commit()
            conn.close()
        
        # 사용자 추가 함수 실행
        def add_users():
            add_user('admintak01', 'pwdforapitest01')
            add_user('admintak02', 'pwdforapitest02')
        
        # 사용자 조회 함수
        def get_user(username):
            conn = sqlite3.connect(DB_FILE)
            cursor = conn.cursor()
            cursor.execute('SELECT * FROM users WHERE username = ?', (username,))
            user = cursor.fetchone()
            conn.close()
            return user
        
        # 실행 코드
        if __name__ == '__main__':
            create_user_table()  # 사용자 테이블 생성
            add_users()          # 사용자 추가​


      • DB(users.db) 데이터 확인
    • Login API 생성
      : Python(Flask)에서 JWT를 통한 인증 구현하기 위해서 Flask-JWT-Extended 라이브러리 사용하겠습니다.
      pip install flask-jwt-extended​
    • Login API 테스트
    from flask import Flask, jsonify, request
    from flask_jwt_extended import *
    from token_users_db import get_user
    
    app = Flask(__name__)
    app.config['JWT_SECRET_KEY'] = 'super-secret'  # JWT를 생성 및 확인하는 데 사용되는 시크릿 키
    jwt = JWTManager(app)
    
    # 로그인 테스트용 엔드포인트
    @app.route('/login', methods=['POST'])
    def login_test():
        username = request.json.get('username', None)
        password = request.json.get('password', None)
        if not username or not password:
            return jsonify({"msg": "Missing username or password"}), 400
    
        user = get_user(username)
        if user and user[2] == password:  # user[2]는 password 필드를 가리킵니다.
    		    access_token = create_access_token(identity=user)  # 사용자 정보를 식별자로 설정
            # 접근 토큰과 함께 로그인 성공 메시지 반환
            return jsonify({"msg": "Login successful", "access_token": access_token}), 200
        else:
            return jsonify({"msg": "Bad username or password"}), 401
    
    if __name__ == '__main__':
        app.run(host="0.0.0.0 or Private IP", Port="5000" ,debug=True)

     

    • Body 값에 사용자 로그인 정보 입력하여 POST METHOD 사용하면, 해당 계정에 대한 Token 발급

     

    • GET, POST METHOD 사용
      • 위에서 로그인할 때, 발급된 Token 값을 METHOD 사용 시, Headers 값에 Key:Vaule 기입합니다.
      • Key:Vaule = Authorization: Bearer access_token
      • "Bearer"는 HTTP 요청에서 사용되는 인증 방식 중 하나입니다. 클라이언트가 서버에 요청을 보낼 때, 인증 정보를 함께 전달하는데, "Bearer" 방식은 이를 위한 표준 형식 중 하나입니다.
    from flask import Flask, jsonify, request
    from flask_jwt_extended import *
    from token_users_db import get_user
    
    app = Flask(__name__)
    app.config['JWT_SECRET_KEY'] = 'super-secret'  # JWT를 생성 및 확인하는 데 사용되는 시크릿 키
    jwt = JWTManager(app)
    
    # 로그인 엔드포인트
    @app.route('/login', methods=['POST'])
    def login():
        username = request.json.get('username', None)
        password = request.json.get('password', None)
        if not username or not password:
            return jsonify({"msg": "Missing username or password"}), 400
    
        user = get_user(username)
        if user and user[2] == password:  # user[2]는 password 필드를 가리킵니다.
            access_token = create_access_token(identity=username)
            return jsonify(access_token=access_token), 200
        else:
            return jsonify({"msg": "Bad username or password"}), 401
    
    # 토큰 인증이 필요한 보호된 엔드포인트 (GET 메서드)
    @app.route('/api/data', methods=['GET'])
    @jwt_required()
    def protected_get():
        current_user = get_jwt_identity()
        return jsonify(logged_in_as=current_user), 200
    
    # 토큰 인증이 필요한 보호된 엔드포인트 (POST 메서드)
    @app.route('/api/data', methods=['POST'])
    @jwt_required()
    def protected_post():
        current_user = get_jwt_identity()
        return jsonify(logged_in_as=current_user), 200
    
    if __name__ == '__main__':
        app.run(host="0.0.0.0 or Private IP", Port="5000" ,debug=True)

     

    3-4. OAuth Authentication

    출처 : https://voyager.postman.com/illustration/oauth-diagram-postman.svg

    • 3rd Party 애플리케이션에 대한 사용자 인증 및 권한 부여를 위한 프로토콜입니다.
    • 클라이언트는 사용자의 인증을 위해 OAuth 서버에 요청하고, 성공하면 액세스 토큰을 받아 API에 접근할 수 있습니다.
    • OAuth는 다양한 시나리오에 대응하기 위해 다양한 인증 흐름을 제공합니다.
    • OAuth 인증 과정을 설명하자면 다음과 같습니다.
      1. 사용자는 특정 Application을 통해 OAuth 인증을 요청합니다.(Authorization Code 요청)

      2. OAuth 서비스는 Redirect URL을 통해 사용자에게 Authorization Code 부여합니다.

      3. 사용자는 2 단계에서 전달받은 Authorization Code를 Authorization Server에게 전달하여 Access Token을 요청합니다.
      4. Authorization Server는 사용자가 Resource Server에 있는 정보를 액세스 할 수 있는 Access Token을 생성하고 전달합니다.
      5. 사용자는 리소스에 대한 액세스를 요청하기 위해  Resource Server에 4단계에서 받은 Access Token을 전달합니다.
      6. Resource Server는 Authorization Server를 통해 Access Token의 유효성을 확인 후, 유효하다면 사용자에게 리소스에 대한 액세스 권한을 부여합니다.

     

    → OAuth 인증에 대해서는 Microsoft Entra ID를 사용하여 보다 자세하게 다룰 예정입니다. 다음 편을 기대해 주세요!


    지금까지 " #01 REST API - 개념과 보안" 에 대해 알아보았습니다.

    다음 편에는 " #02 REST API - 활용"으로 돌아오겠습니다 🙉 (!!많관부!!) 

    여러분의 생각하는 부족한 점, 궁금한 점 등 자유로운 의견을 남겨주세요!

    728x90
    320x100
    SMALL

    'TOPIC > Code' 카테고리의 다른 글

    [Terraform] 전체 코드 중 특정 resource/module만 apply하기  (0) 2023.12.15