[Flask] 4일차. Flask Authentication

김민정·2024년 3월 5일
0

Flask

목록 보기
4/5
post-thumbnail

01. Session 인증

1. Session 방식의 인증

세션 인증은 웹 애플리케이션에서 사용자의 상태를 유지하기 위해 주로 사용된다. 세션 인증은 사용자가 로그인하면 서버 측에서 그 사용자의 상태(인증 정보)를 기록하고, 사용자가 로그아웃하면 그 상태를 제거하는 방식으로 작동한다. 이를 통해 사용자가 애플리케이션 내에서 다양한 요청을 수행할 때 로그인 상태를 유지할 수 있다.

  • 세션의 필요성
    : 세션은 사용자가 웹 사이트에 접속해 있는 동안 서버에 유지되는 상태 정보다. 웹은 기본적으로 상태가 없는(stateless) 프로토콜이므로, 웹 서버는 HTTP 요청 간 사용자의 상태를 기본적으로 기억하지 못한다. 세션은 이러한 한계를 극복하기 위해 사용자의 상태(예: 로그인 여부, 사용자 설정 등)를 서버에 저장한다.

  • 세션 인증의 사용 사례

    • 웹 사이트 로그인
      : 사용자가 웹 사이트에 로그인하고, 사이트 탐색 중에 로그인 상태를 유지해야 할 때
    • 개인화된 사용자 경험
      : 사용자의 선호, 설정 등을 기억하여 개인화된 컨텐츠를 제공할 때
    • 장바구니
      : 온라인 쇼핑 사이트에서 사용자의 장바구니 정보를 기억
  • 세션 인증의 단점

    • 보안상의 문제
      • 브라우저는 세션 ID를 쿠키에 저장하므로, 쿠키가 해킹되지 않도록 주의해야한다.
      • 세션 데이터는 사용자의 브라우저에 저장되므로 중요한 정보(비밀번호, 결제 정보 등)는 저장해서는 안된다.
      • 암호화를 위해 사용하는 SECRET_KEY 유출의 위험이 존재한다.
    • 서버의 리소스를 많이 잡아먹는 문제
      • 각 사용자에 대한 세션 정보를 관리하기 위해 서버의 리소스가 많이 사용된다.
      • 세션이 브라우저 쿠키에 저장되기 때문에 세션 데이터가 크기가 너무 커지지 않도록 주의해야한다.
  • 세션 인증의 동작 방식

    1. 로그인
      : 사용자가 로그인 폼을 통해 자신의 자격 증명(예: 사용자 이름, 비밀번호)을 제출하면, 서버는 이를 검증하고 세션에 사용자의 인증 정보를 저장한다. 이 정보는 서버의 메모리나 데이터베이스 등에 저장될 수 있다.

    2. 세션 ID
      : 사용자의 인증 정보가 세션에 저장되면, 서버는 고유한 세션 ID를 생성하여 사용자의 브라우저에 쿠키 형태로 보낸다. 사용자는 이후의 모든 요청에서 이 세션 ID를 서버에 전송한다.

    3. 상태 유지
      : 서버는 받은 세션 ID를 사용하여 사용자의 인증 상태와 관련 데이터를 조회한다. 이를 통해 사용자가 로그인한 상태인지 확인하고, 해당 사용자에게 적절한 응답을 제공할 수 있다.

    4. 로그아웃
      : 사용자가 로그아웃하면, 서버는 해당 사용자의 세션을 제거하고 세션 ID를 무효화한다.

2. Flask에서 제공하는 기본 Session 인증 방법

flask에서 제공하는 기본 session 모듈을 사용하면 세션 인증이 가능하다.

  • app.py

    from flask import Flask, render_template, request, redirect, url_for, session, flash
    
    app = Flask(__name__)
    
    # from datetime import timedelta
    # app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)  # 예: 7일
    
    app.secret_key = 'your_secret_key'  # 실제 배포시에는 .env or yaml 파일에 따로 저장 필요
    
    # 예시 사용자 데이터
    users = {
      "john": "pw123",
      "leo": "pw123"
    }
    
    @app.route('/')
    def index():
      return render_template('login.html')
      
    # 사용자가 login.html 폼을 통해 로그인 정보를 제출하면, login 뷰에서 이를 검증
    # 검증이 성공하면, 사용자 이름을 세션에 저장하고 비밀 페이지로 리디렉션
    @app.route('/login', methods=['POST'])
    def login():
      username = request.form['username']
      password = request.form['password']
      if username in users and users[username] == password:
          session['username'] = username
          # session.permanent = True  # 세션 유지 기간을 활성화
          return redirect('/secret')
      else:
          flash('Invalid username or password')
          return redirect('/')
          
    # 인증된 사용자 페이지
    # secret 뷰에서는 사용자가 로그인되어 있는지 세션을 통해 확인하고, 
    # 로그인되지 않은 사용자는 로그인 페이지로 리디렉션합니다.
    @app.route('/secret')
    def secret():
      if 'username' in session:
          return render_template('secret.html')
      else:
          return redirect('/')
          
    # 사용자가 로그아웃 링크를 클릭하면, 
    # logout 뷰에서 세션에서 사용자 정보를 제거하고 초기 페이지로 리디렉션
    @app.route('/logout')
    def logout():
      session.pop('username', None)
      return redirect('/')
      
    if __name__ == '__main__':
      app.run(debug=True)
  • templates/login.html

    <!DOCTYPE html>
    <html>
      <head>
          <title>Login</title>
      </head>
      <body>
          <h2>Login</h2>
          <form method="POST" action="/login">
              <input type="text" name="username" placeholder="Username" required>
              <input type="password" name="password" placeholder="Password" required>
              <button type="submit">Login</button>
          </form>
      </body>
    </html>
  • templates/secret.html

    <!DOCTYPE html>
    <html>
      <head>
          <title>Secret Page</title>
      </head>
      <body>
          <h2>Welcome to the Secret Page!</h2>
          <a href="/logout">Logout</a>
      </body>
    </html>
  • 성공적으로 로그인이 완료되면 쿠키에 session 데이터가 생성되어 있는 것을 볼 수 있다.

  • 로그아웃 버튼을 클릭하면 쿠키에 있는 session 값은 저절로 사라진다.

3. Flask 기본 세션 모듈에서 제공하는 함수

session 객체는 사용자의 브라우저에 저장된 쿠키와 연결되어 있으며, 사전(dictionary) 형태로 작동하여 세션 데이터를 저장하고 접근한다.

(1) session에 데이터 저장하기

  • 데이터 추가
    : session 객체에 새로운 키와 값을 추가할 수 있다.
    session['username'] = 'john'

(2) session에서 데이터 가져오기

  • 데이터 읽기
    : session 객체에서 키를 사용하여 데이터를 읽을 수 있다.
    username = session['username']
  • get 메소드 사용
    : 키가 존재하지 않을 경우 None을 반환하도록 get 메소드를 사용할 수 있다.
    username = session.get('username')

(3) session에서 데이터 제거하기

  • 데이터 삭제
    : pop 메소드를 사용하여 특정 키와 그에 대응하는 값을 세션에서 제거할 수 있다.
    session.pop('username', None)
  • 세션 클리어
    : clear 메소드를 사용하여 세션의 모든 데이터를 제거할 수 있다.
    session.clear()
    • 특정 사용자와 관련된 모든 세션 데이터를 서버 측에서 삭제하는 데 사용한다. 주로 사용자가 로그아웃할 때, 사용자와 관련된 모든 세션 데이터를 삭제한다.

(4) 세션 유지 기간 설정

permanent 속성을 True로 설정하여 세션의 유지 기간을 PERMANENT_SESSION_LIFETIME 설정값에 따라 조정 가능하다.

  • app.py

    from datetime import timedelta
    
    app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)  # 예: 7일
  • routes/user.py

    @app.route('/login', methods=['POST'])
    def login():
      session['username'] = 'your_username'
      session.permanent = True  # 세션 유지 기간을 활성화
      return redirect(url_for('secret'))

02. HTTP 기본 인증(Basic Authentication, Flask-HTTPAuth)

1. Flask-HTTPAuth란?

Flask에서의 HTTP 기본 인증(Basic Authentication) 방법이다. 서버에 요청을 보낼 때 HTTP 헤더에 사용자 이름과 비밀번호를 함께 전송하여 인증을 수행한다. 이때 Flask의 flask_httpauth 라이브러리를 사용하여 기본 인증을 구현한다.

2. 필요한 라이브러리 설치

> pip install Flask-HTTPAuth

위 코드로 Flask-HTTPAuth 라이브러리를 설치할 수 있다.

3. Flask-HTTPAuth 인증 설정 방법

  • app.py

    from flask import Flask, jsonify, render_template
    from flask_httpauth import HTTPBasicAuth
    
    app = Flask(__name__)
    auth = HTTPBasicAuth()
    
    # 사용자 정보
    users = {
      "admin": "secret",
      "guest": "guest"
    }
    
    @auth.verify_password
    def verify_password(username, password):
      if username in users and users[username] == password:
          return username
          
    @app.route('/protected')
    @auth.login_required
    def protected():
      return render_template('secret.html')
      
    @app.route('/')
    def index():
      return render_template('index.html')
      
    if __name__ == '__main__':
      app.run(debug=True)
  • @auth.verify_password (사용자 인증)
    : 사용자 이름과 비밀번호가 유효한지 확인하는 함수를 정의한다. 여기서는 간단한 사전 users를 사용하여 사용자 이름과 비밀번호를 확인한다. 실전 환경에서는 데이터베이스 또는 다른 안전한 저장소를 사용해야한다.

  • @auth.login_required (라우트 보호)
    : 인증된 사용자만 해당 라우트로 접근할 수 있도록하는 목적으로 사용자 인증을 요구한다.

  • @auth.error_handler (오류 핸들링)
    : 인증에 실패했을 때의 동작을 정의한다.
    위 코드에서는 403 상태 코드와 함께 오류 메시지를 반환

보안을 강화하기 위해서는 HTTPS를 사용하는 것이 좋으며, 실제 프로덕션 환경에서는 더 견고한 인증 방식(예: OAuth, JWT)을 사용하는 것이 권장된다.

  • templates/index.html
    <!DOCTYPE html>
    <html>
      <head>
        <title>Home Page</title>
      </head>
      <body>
        <h1>Welcome to the Home Page</h1>
        <a href="/protected">Go to Protected Resource</a>
      </body>
    </html>
  • templates/secret.html
    <!DOCTYPE html>
    <html>
    <head>
      <title>Secret Page</title>
    </head>
    <body>
      <h1>Secret Page</h1>
      <p>Welcome to the secret page!</p>
      <!-- 여기에 보호된 콘텐츠나 기능을 추가할 수 있습니다. -->
      <a href="/">Home</a>
      <a href="/logout">Logout</a>
    </body>
    </html>
    1. API 인증
      : RESTful API나 다른 HTTP 기반 API에서 클라이언트의 요청을 인증하는 데 사용된다. 클라이언트는 HTTP 헤더에 인증 정보(기본 인증, 토큰 인증 등)를 포함시켜 요청을 보내고, 서버는 이를 검증하여 적절한 응답을 제공한다.
    2. 간단한 웹 애플리케이션 인증
      : 사용자 인터페이스가 복잡하지 않거나 사용자 인증을 위한 별도의 로그인 폼이 필요 없는 간단한 웹 애플리케이션에서 사용된다. 예를 들어, 관리자 페이지나 특정 문서에 대한 접근을 제한하는 경우에 적합하다.

03. Flask-Login 사용

1. Flask-Login이란?

사용자 인증을 쉽게 관리할 수 있도록 도와주는 라이브러리이다. 사용자 로그인 및 로그아웃 프로세스를 처리하고, 현재 로그인한 사용자의 정보에 접근 가능하게 해준다.

2. Flask-Login 설치

> pip install flask-login

위 코드로 Flask-Login 라이브러리를 설치할 수 있다.

3. Flask-Login 적용 방법

[파일 구조]

flask_login/
│
├── app.py
├── models.py
├── routes.py
└── templates/
    ├── index.html
    └── login.html
  • app.py

    from flask import Flask
    from flask_login import LoginManager
    from models import User
    
    # 'routes' 모듈을 임포트하기 전에 'app'과 'login_manager' 객체를 생성해야 함
    app = Flask(__name__)
    app.secret_key = 'your_secret_key'
    
    login_manager = LoginManager()
    login_manager.init_app(app)
    login_manager.login_view = 'login'
    
    @login_manager.user_loader
    def load_user(user_id):
      return User.get(user_id)
      
    # 이제 'routes' 모듈을 임포트
    from routes import configure_routes
    configure_routes(app)
    
    if __name__ == "__main__":
      app.run(debug=True)
  • models.py

    from flask_login import UserMixin
    
    users = {'admin': {'password': 'secret'}}
    
    class User(UserMixin):
      def __init__(self, username):
          self.id = username
    
      @staticmethod
      def get(user_id):
          if user_id in users:
              return User(user_id)
          return None
  • routes.py

    from flask import render_template, redirect, url_for, request, flash
    from flask_login import login_user, logout_user, login_required
    from models import User, users
    
    def configure_routes(app):
      @app.route('/')
      def index():
          return render_template('index.html')
    
      @app.route('/login', methods=['GET', 'POST'])
      def login():
          if request.method == 'POST':
              username = request.form['username']
              password = request.form['password']
              user = User.get(username)
              if user and users[username]['password'] == password:
                  login_user(user)
                  return redirect(url_for('index'))
              else:
                  flash('Invalid username or password')
          return render_template('login.html')
    
      @app.route('/logout')
      def logout():
          logout_user()
          return redirect(url_for('index'))
    
      @app.route('/protected')
      @login_required
      def protected():
          return '<h1>Protected area</h1> <a href="/logout">Logout</a>'
  • index.html

    <!DOCTYPE html>
    <html>
    <head>
      <title>Home Page</title>
    </head>
    <body>
      <h1>Welcome to the Home Page</h1>
      <a href="/login">Login</a> | 
      <a href="/logout">Logout</a> |
      <a href="/protected">Protected Page</a>
    </body>
    </html>
  • login.html

    <!DOCTYPE html>
    <html>
    <head>
      <title>Login</title>
    </head>
    <body>
      <h1>Login</h1>
      <form method="post">
        Username: <input type="text" name="username" /><br />
        Password: <input type="password" name="password" /><br />
        <input type="submit" value="Login" />
      </form>
    </body>
    </html>
  • 테스트 방법

    • 홈페이지 접근: 브라우저에서 http://127.0.0.1:5000/으로 접속한다.
    • 로그인
      : 'Login' 링크를 클릭하여 로그인 페이지로 이동한 다음, 유효한 사용자 이름과 비밀번호를 입력한다.
      (예시: username: admin, password: secret).
    • 보호된 페이지 접근
      : 인증된 사용자는 'Protected Page' 링크를 클릭하여 보호된 페이지에 접근할 수 있다.
    • 로그아웃
      : 'Logout' 링크를 클릭하여 로그아웃할 수 있다.

    위 예제는 Flask-Login의 기본적인 사용 방법을 보여주는 예제다. 실제 서비스 운영시에는 데이터베이스와 함께 사용자 인증 로직을 더 복잡하게 구성할 수 있다.


04. JWT-Extended

1. JWT-Extended란?

JSON Web Tokens (JWT)을 쉽게 다룰 수 있도록 해주는 확장 라이브러리다.

  • JWT
    : 사용자 인증 및 권한 부여에서 널리 사용되는 방식으로 서버와 클라이언트 간의 안전한 정보 전달을 가능하게 한다.

2. 필요한 라이브러리 설치

> pip install Flask-JWT-Extended

3. JWT-Extended 적용 과정

[파일 구조]

JWT-Extended/
│
├── app.py
├── jwt_utils.py
├── models/user.py
├── routes/user.py
└── templates/
    ├── index.html
    ├── login.html
    └── protexted.html
  • app.py

    from flask import Flask, render_template
    from routes.user import user_bp
    from jwt_utils import configure_jwt  # JWT 설정 함수를 임포트합니다.
    
    app = Flask(__name__)
    configure_jwt(app)  # JWT 관련 추가 설정을 적용합니다.
    
    app.register_blueprint(user_bp, url_prefix='/user')
    
    @app.route('/')
    def index():
      return render_template('index.html')
      
    if __name__ == '__main__':
      app.run(debug=True)
  • models/user.py
    : 사용자 모델 정의

    class User:
      def __init__(self, id, username, password):
          self.id = id
          self.username = username
          self.password = password
  • jwt_utils.py
    : JWT 관련 유틸리티 함수와 설정 정의

    from flask_jwt_extended import JWTManager
    from blocklist import BLOCKLIST
    from flask import jsonify
    
    jwt = JWTManager()
    
    def configure_jwt(app):
      app.config["JWT_SECRET_KEY"] = "your-secret-key"
      jwt.init_app(app)
    
      # token expired time settings
      freshness_in_minutes = 1
      app.config["JWT_ACCESS_TOKEN_EXPIRES"] = freshness_in_minutes * 60 # 1 hour
      jwt.init_app(app)
    
      # claim: 청구하다
      # 추가적인 정보를 토큰에 넣고 싶을 때 사용
      @jwt.additional_claims_loader # @데코레이터
      def add_claims_to_jwt(identity):
          if identity == 1:
              return {"is_admin": True}
          return {"is_admin": False}
    
      # 토큰이 블록리스트에 있는지 확인하는 함수
      # 블록리스트에 있으면 해당 토큰이 유효하지 않다고 판단
      @jwt.token_in_blocklist_loader
      def check_if_token_in_blocklist(jwt_header, jwt_payload):
          # jti=jwt id
          return jwt_payload["jti"] in BLOCKLIST
    
      # 만료된 토큰이 사용되었을 때 실행되는 함수
      @jwt.expired_token_loader
      def expired_token_callback(jwt_header, jwt_payload):
          return jsonify({"msg": "Token expired", "error": "token_expired"}), 401
    
      # 유효하지 않은 토큰이 사용되었을 때 실행되는 함수
      # 토큰의 서명이나 구조가 유효하지 않을 때 실행됩니다. 주로 토큰 자체의 문제로 발생하는 경우에 해당합니다.
      @jwt.invalid_token_loader
      def invalid_token_callback(error):
          return (
              jsonify(
                  {"message": "Invalid token", "error": "invalid_token"}
              ),
              401,
          )
    
      # 해당 토큰으로 접근 권한이 없는 경우
      @jwt.unauthorized_loader
      def missing_token_callback(error):
          return (
              jsonify(
                  {
                      "description": "Access token required",
                      "error": "access_token_required",
                  }
              ),
              401,
          )
    
      # fresh한 토큰이 필요한데 fresh하지 않은 토큰이 사용되었을 때 실행되는 함수를 정의합니다. 
      # 해당 응답을 반환하여 fresh한 토큰이 필요하다는 메시지를 전달
      # JWT_ACCESS_TOKEN_EXPIRES으로 토큰 만료 시간 조정
      @jwt.needs_fresh_token_loader
      def token_not_fresh_callback(jwt_header, jwt_payload):
          return (
              jsonify(
                  {"description": "Token is not fresh.", "error": "fresh_token_required"}
              ),
              401,
          )
    
      # 토큰이 폐기되었을 때 실행되는 함수를
      @jwt.revoked_token_loader
      def revoked_token_callback(jwt_header, jwt_payload):
          return (
              jsonify(
                  {"description": "Token has been revoked.", "error": "token_revoked"}
              ),
              401,
          )
  • blocklist.py
    : jwt_utils.py 파일 안에 있는 blocklist 관련 함수 정의, 토큰 관리를 도와주는 파일

    BLOCKLIST = set()
    
    def add_to_blocklist(jti):
      BLOCKLIST.add(jti)
    
    def remove_from_blocklist(jti):
      BLOCKLIST.discard(jti)
    • jti는 JWT의 고유 값들을 의미한다. JWT를 관리할 수 있게 도와주는 고유 번호라고 생각하면 된다.
  • routes/user.py

    from flask import Blueprint, jsonify, request, render_template
    from flask_jwt_extended import create_access_token, create_refresh_token, jwt_required, get_jwt_identity
    from models.user import User
    
    user_bp = Blueprint('user', __name__)
    
    # 임시 사용자 데이터
    users = {
      'user1': User('1', 'user1', 'pw123'),
      'user2': User('2', 'user2', 'pw123')
    }
    
    @user_bp.route('/login', methods=['GET', 'POST'])
    def login():
      if request.method == 'POST':
          username = request.json.get('username', None)
          password = request.json.get('password', None)
    
          user = users.get(username)
          if user and user.password == password:
              access_token = create_access_token(identity=username)
              refresh_token = create_refresh_token(identity=username)
              return jsonify(access_token=access_token, refresh_token=refresh_token)
          else:
              return jsonify({"msg": "Bad username or password"}), 401
      else:
          return render_template('login.html')
          
          
    @user_bp.route('/protected', methods=['GET'])
    @jwt_required()
    def protected():
      current_user = get_jwt_identity()
      return jsonify(logged_in_as=current_user), 200
      
    @user_bp.route('/protected_page')
    def protected_page():
      return render_template('protected.html')
      
    from flask_jwt_extended import get_jwt
    from blocklist import add_to_blocklist  # 블랙리스트 관리 모듈 임포트
    @user_bp.route('/logout', methods=['POST'])
    @jwt_required()
    def logout():
      jti = get_jwt()["jti"]
      add_to_blocklist(jti)  # jti를 블랙리스트에 추가
      return jsonify({"msg": "Successfully logged out"}), 200
  • templates

    • index.html

      			<!DOCTYPE html>
      			<html>
      			  <head>
      			    <title>Home Page</title>
      			  </head>
      			  <body>
      			    <h1>Welcome to the Home Page</h1>
      			    <a href="/user/login">Login</a> | <a href="/user/logout">Logout</a>
      			    <a href="/user/protected">Protected Page</a>
      			  </body>
      			</html>
      			```
      
    • login.html

      <!DOCTYPE html>
      <html>
        <head>
          <title>Login</title>
          <script>
            function handleLogin(event) {
              event.preventDefault(); // 폼의 기본 제출 동작을 방지
      
              // 폼 데이터를 JSON으로 변환
              var username = document.getElementById("username").value;
              var password = document.getElementById("password").value;
              var data = { username: username, password: password };
      
              // fetch를 사용하여 서버에 POST 요청 보내기
              fetch("/user/login", {
                method: "POST",
                headers: {
                  "Content-Type": "application/json",
                },
                body: JSON.stringify(data),
              })
                .then((response) => response.json())
                .then((data) => {
                  console.log("Success:", data);
                  // 로그인 성공 후 'protected' 페이지로 리다이렉트
                  localStorage.setItem("access_token", data.access_token);
                  localStorage.setItem("refresh_token", data.refresh_token);
                  window.location.href = "/user/protected_page";
                })
                .catch((error) => {
                  console.error("Error:", error);
                });
            }
          </script>
        </head>
        <body>
          <h1>Login</h1>
          <form onsubmit="handleLogin(event)">
            Username: <input type="text" id="username" name="username" /><br />
            Password: <input type="password" id="password" name="password" /><br />
            <input type="submit" value="Login" />
          </form>
        </body>
      </html>
      			```
      
    • protexted.html

      <!DOCTYPE html>
      <html>
        <head>
          <title>Protected Page</title>
          <script>
            document.addEventListener("DOMContentLoaded", function () {
              const token = localStorage.getItem("access_token");
              console.log("token", token);
              if (token) {
                fetch("/user/protected", {
                  headers: {
                    Authorization: `Bearer ${token}`,
                  },
                })
                  .then((response) => {
                    if (response.ok) {
                      return response.json();
                    } else {
                      throw new Error("Access Denied");
                    }
                  })
                  .then((data) => {
                    document.getElementById("content").innerHTML =
                      "Welcome, " + data.logged_in_as;
                  })
                  .catch((error) => {
                    document.getElementById("content").innerHTML = "Access Denied";
                    console.error("Error:", error);
                  });
              } else {
                document.getElementById("content").innerHTML =
                  "No token found, please login.";
              }
            });
          </script>
        </head>
        <body>
          <h1>This is a Protected Page</h1>
          <div id="content">
            <p>Loading...</p>
          </div>
          <button onclick="logout()">Logout</button>
        </body>
        <script>
          // 로그아웃 함수
          function logout() {
            // 로컬 스토리지에서 JWT 토큰 제거
            localStorage.removeItem("access_token");
            localStorage.removeItem("refresh_token");
      
            // 로그인 페이지 또는 홈페이지로 리다이렉트
            window.location.href = "/";
          }
        </script>
      </html>
      			```
      
  • 정리
    Flask JWT-Extended를 사용하여 간단한 JWT 기반 인증 시스템을 구축한다.

    1. app.py에서 Flask 애플리케이션을 설정하고 JWT 인증 라우트를 정의한다.

    2. models/user.py에서 사용자 모델을 정의한다.

    3. jwt_utils.py에서 JWT 설정과 인증 관련 유틸리티 함수를 정의한다.

4. JWTManager에서 사용할 수 있는 주요 데코레이터

(1) @jwt_required()

from flask_jwt_extended import jwt_required

@jwt_required()
def protected_route():
    # 이 라우트는 JWT가 필요하다.
    # JWT가 유효하면 실행된다.

이 데코레이터는 해당 엔드포인트에 접근하려면 JWT가 필요하다는 것을 나타낸다. 클라이언트는 유효한 JWT를 제공해야만 해당 라우트에 접근할 수 있다.

(2) @jwt_optional()

from flask_jwt_extended import jwt_optional

@jwt_optional()
def optional_route():
    # 이 라우트는 JWT가 선택적이다.
    # JWT가 제공되면 유효성을 확인하고, 제공되지 않으면 계속 진행한다.

이 데코레이터는 해당 엔드포인트에 클라이언트가 JWT를 제공할 수 있지만, 필수적이지 않다는 것을 나타낸다. 클라이언트가 JWT를 제공하면 이를 확인하고 유효성을 검사하며, 제공되지 않으면 계속 진행한다.

(3) @fresh_jwt_required()

from flask_jwt_extended import fresh_jwt_required

@fresh_jwt_required()
def fresh_route():
    # 이 라우트는 fresh한 JWT가 필요하다.
    # JWT가 fresh하지 않으면 해당 라우트에 접근할 수 없다.

이 데코레이터는 해당 엔드포인트에 접근하려면 fresh한 JWT가 필요하다는 것을 나타낸다. JWT가 fresh하지 않으면 해당 라우트에 접근할 수 없다. 또한 @jwt_required(optional=True), @jwt_required(fresh=True, optional=True) 등의 옵션을 사용하여 더 세부적인 제어가 가능하다.

(4) @jwt_refresh_token_required()

from flask_jwt_extended import jwt_refresh_token_required

@jwt_refresh_token_required()
def refresh_route():
    # 이 라우트는 refresh token이 필요하다.
    # refresh token이 유효하면 해당 라우트에 접근할 수 있다.

이 데코레이터는 해당 엔드포인트에 접근하려면 refresh token이 필요하다는 것을 나타낸다. refresh token이 유효하면 해당 라우트에 접근할 수 있다.


[4일차 후기]

본격적으로 로그인, 로그아웃, 쿠키 생성, 접근 관리 등을 하다보니 재밌다!


[참고 자료]

  • [오즈스쿨 스타트업 웹 개발 초격차캠프 백엔드 Flask 강의]
profile
백엔드 코린이😁

0개의 댓글