TOP
class="layout-aside-left paging-number">
본문 바로가기
[파이썬 Projects]/<파이썬 웹개발>

[파이썬] 플라스크(Flask) - 인증과 세션(2)

by 기록자_Recordian 2024. 7. 21.
728x90
반응형
시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.

이전 내용
 

[파이썬] 플라스크(Flask) - 인증과 세션

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.인증과 세션 인증(authentication)사용자가 누구인지 확

puppy-foot-it.tistory.com


세션을 이용한 상태 관리

 

플라스크의 session 객체는 사용자별 상태를 관리하기 위한 수단으로, 쿠키에 암호화된 형태로 데이터를 저장한다.

이는 사용자가 다시 사이트에 방문했을 때 이전의 상태를 유지할 수 있게 해준다.

 

플라스크 애플리케이션을 설정할 때 SECRET_KEY를 지정해야 하는데, 이는 세션 데이터를 암호화하는 데 사용된다.

app.config['SECRET_KEY'] = 'mysecretkey'

 

로그인에 성공하면, 해당 사용자의 정보를 세션에 저장할 수 있다.

(user_id를 예로 들었을 경우)

from flask import session

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User.query.filter_by(username=username).first()
        if user and user.check_password(password): # check_password는 예시
            session['user_id'] = user.id
            return redirect(url_for('index'))
            login_user(user) # 사용자가 존재하고 비밀번호가 맞다면 로그인 처리
    return render_template('login.html')

 

세션에 저장된 정보는 애플리케이션의 다른 부분에서도 사용할 수 있다.

@app.route('/index')
def index():
    user_id = session.get('user_id')
    if user_id:
        user = User.query.get(user_id)
        return f'Hello, {user.username}'
    return 'You are not logged in'

 

[Flask-Login과 세션의 차이]

  • Flask-Login: 이 확장은 사용자 인증을 관리하는 고수준의 인터페이스를 제공.로그인 상태 유지, 사용자 리디렉션, 'Remember Me' 기능 등 여러 복잡한 인증 작업을 간단하게 만들어 준다.
  • 세션: 이는 낮은 수준의 상태 관리 매커니즘을 제공한다. 즉, 개발자가 직접 세션 데이터를 설정, 조회, 제거해야 한다.

또한, Flask-Login은 내부적으로 session을 사용해 사용자 상태를 저장하고 관리한다.

예를 들어 login_user() 함수를 호출하면 Flask-Login은 사용자의 ID를 세션에 저장한다. 

 

Flask-Login을 사용하면 복잡한 인증 로직을 쉽게 처리할 수 있지만, 일반적인 상태 관리에는 세션이 더 적합할 수 있다.

(예. 장바구니 항목, 사용자 설정 등)


예제로 이해하는 세션

 

로그인 성공 시에 세션에 user_id를 저장하고, 로그아웃 시에는 세션에서 user_id를 제거한다.

★ 사전에 MySQL 데이터베이스에 flaskdb가 있어야 하고, user테이블에는 아무 데이터도 없어야 한다.

(만약 데이터가 있을 경우, 기존 데이터를 DELETE FROM user 등의 SQL 구문으로 삭제한다.)

from flask import Flask, request, redirect, url_for, session # 필요한 모듈
from flask_sqlalchemy import SQLAlchemy # ORM을 위한 플라스크 확장
import pymysql

# 사용자 인증 관리
from flask_login import LoginManager, login_required, login_user, logout_user, UserMixin, current_user

# 플라스크 애플리케이션 인스턴스 생성
app = Flask(__name__)
# 데이터베이스 연결 URI 설정
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:pw@localhost:3306/flaskdb'
# mysql://root:[비밀번호]@localhost:[root번호]/[db이름]

# SQLAlchemy의 수정 추적 기능 비활성화
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Flask 애플리케이션을 위한 비밀 키 설정 (세션 및 쿠키에 대한 보안 향상을 위함)
app.config['SECRET_KEY'] = 'mysecretkey'

# SQLAlchemy 인스턴스 생성 후 애플리케이션에 바인딩
db = SQLAlchemy(app)

# LoginManager 인스턴스 생성
login_manager = LoginManager() # Flask 애플리케이션과 LoginManager 인스턴스 연결
login_manager.init_app(app) # 애플리케이션에 LoginManager 적용
login_manager.login_view = 'login' # 로그인 페이지의 뷰 함수 이름 설정

# 데이터베이스 모델 정의
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True) # 사용자 id 기본키로 설정
    username = db.Column(db.String(80), unique=True, nullable=False) # 사용자 이름, 중복 불가 및 필수 입력
    email = db.Column(db.String(120), unique=True, nullable=False) # 이메일 주소, 중복 불가 및 필수 입력
    password = db.Column(db.String(128), nullable=False) # 사용자 비밀번호, 필수 입력
    
    def __repr__(self):
        return f'<User {self.username}>' # 객체를 문자열로 표현할 때 사용할 형식

# 사용자 ID로 사용자를 로드하는 콜백 함수 정의(사용자 로드 함수에 데코레이터 적용)
@login_manager.user_loader
def load_user(user_id):
    # 주어진 user_id로 사용자 조회 후 반환
    return User.query.get(int(user_id))

# 애플리케이션 컨텍스트 안에서 데이터베이스 테이블 생성
with app.app_context():
    db.create_all()

# 인덱스 뷰 정의
@app.route('/')
def index():
    user_id = session.get('user_id')
    if user_id:
        user = User.query.get(user_id)
        return f'Logged in as {user.username}' # 로그인된 상태 표시
    return 'You are not logged in' # 로그인되지 않은 상태 표시

# 보호된 페이지를 위한 뷰 정의 (로그인 필수)
@app.route('/protected')
@login_required # 로그인한 사용자만 액세스 가능
def protected():
    # 현재 로그인한 사용자의 이름 표시
    return f'Logged in as {current_user.username}'

# 로그인 뷰 정의
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        # 데이터베이스에서 사용자 조회
        user = User.query.filter_by(username=username).first()
        # 사용자가 존재하고 비밀번호가 맞다면
        if user and user.password == password:
            login_user(user) # 로그인 처리
            session['user_id'] = user.id # 세션에 user_id 저장
            return redirect(url_for('protected')) # 보호된 페이지 라다이렉트
        
    # 로그인 폼 HTML 반환
    return '''
        <form method="post">
            Username: <input type="text" name="username"><br>
            Password: <input type="password" name="password"><br>
            <input type="submit" value="Login">
         </form>   
    '''    

# 로그아웃 뷰 정의
@app.route('/logout')
@login_required # 로그인한 사용자만 액세스 가능
def logout():
    logout_user() # 현재 사용자 로그아웃 처리
    session.pop('user_id', None) # 세션에서 user_id 제거
    return redirect(url_for('index')) # 홈페이지로 리디렉션

# 테스트 사용자를 생성하는 뷰 정의
@app.route('/create_test_user')
def create_test_user():
    test_user = User(username='testuser', email='test@example.com', password='testpassword') # 테스트 사용자 생성
    db.session.add(test_user)
    db.session.commit() # 데이터베이스에 테스트 사용자 추가
    return 'Test user created' # 사용자 생성 완료 메시지 반환

 

위의 코드를 app.py로 저장한 후, 터미널에서 flask run 실행

실행 후 

http://127.0.0.1:5000 에 접속하면 로그인 하지 않았다는 메시지를 받게 되고,

http://127.0.0.1:5000/create_test_user 에 접속하면 사용자 계정이 생성되었다는 메시지를 받게 되고,

http://127.0.0.1:5000/login 에 접속하여 새로 생성한 계정 (testuser1)을 로그인하면

http://127.0.0.1:5000/protected 로 넘어가면서 해당 계정명으로 로그인 되었다는 메시지를 받는다

http://127.0.0.1:5000/ 로 접속하면 현재 로그인 된 상태이므로, 현재 testuser1 계정으로 로그인되었다는 메시지를 받고

 

http://127.0.0.1:5000/logout 에 접속하면 자연스레 로그아웃 되면서 계정이 로그인된 상태가 아니라는 메시지를 받는다.


세션 관련 문법과 동작

 

  • 세션 저장: session은 딕셔너리 형태로 사용할 수 있으며, seesion['key'] = value 형태로 값을 저장할 수 있다
  • 세션 조회: session.get('key') 또는 session['key'] 형태로 값을 조회한다. get() 메서드를 사용하면 키가 존재하지 않을 경우 None을 반환한다
  • 세션 제거: session.pop('key', None) 형태로 값을 제거한다. pop() 메서드는 키가 존재하면 해당 값을 제거하고 반환한다. 존재하지 않으면 두번째 인자(여기서는 None)를 반환한다
  • 세션 유지: 플라스크 session은 쿠키를 통해 클라이언트에 저장된다. 서버는 이 쿠키를 암호화하여 보안을 유지하고, 애플리케이션의 SECRET_KEY를 이용해 암호화의 복호화를 수행한다.

이러한 session을 통해 사용자의 로그인 상태를 유지하고 관리할 수 있다.

 

728x90
반응형