YOGYUI

Flask - extension을 이용한 HTTP 인증 절차 구현 본문

Software/Python

Flask - extension을 이용한 HTTP 인증 절차 구현

요겨 2021. 1. 8. 01:44
반응형

Flask web server 구현 시 인증 절차를 위해 데코레이터 함수를 직접 구현하는게 일반적이다

관련 링크: https://eddmann.com/posts/using-basic-auth-and-decorators-in-pythons-flask/

 

하지만 IoT 서버 구동과 같은 단순한 인증 절차만 필요할 경우 Flask_HTTPAuth같은 extension을 설치해 간단하게 해결할 수 있다 (로그아웃같은 기능은 session 별도 구현 필요)

 

본 포스팅에서는 Flask_HTTPAuth를 활용한 HTTP Basic 인증에 대한 예시만 다룬다

HTTP Digest 인증, (JWS/JWT) 토큰 인증 방식 / user-role 등에 대한 내용은 공식 페이지에서 참고하도록 한다

 

[Structure]

+ proj

    + templates

        - index.html

        - result.html

    - app.py         

 

1. Flask 서버 코드 구현

# app.py
from flask import Flask, render_template
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import generate_password_hash, check_password_hash

app = Flask(__name__)
auth = HTTPBasicAuth()

users = {
    "user1": generate_password_hash("password1")
}


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/login')
@auth.login_required
def login():
    user = auth.current_user()
    return render_template('result.html', user=user)


@auth.verify_password
def verify_password(username, password):
    if username in users and check_password_hash(users.get(username), password):
        print('verify_password success >> username: {}, password: {}'.format(username, password))
        return username  # If the credentials belong to a user, then the function should return the user object.
    print('verify_password failed >> username: {}, password: {}'.format(username, password))
    return None  # If the credentials are invalid the functon can return None or False.


if __name__ == '__main__':
    app.run(host='127.0.0.1', port=9999, debug=True)

2. HTML 코드 작성

<!-- index.html -->
<!DOCTYPE html>
<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <h1>Authentication Test Module</h1>
        <form method="get" action="/login" enctype="text/plain">
            <input type="submit" value="LOGIN" name="">
        </form>
    </body>
</html>
<!-- result.html -->
<!DOCTYPE html>
<html>
    <head>
        <title>Result</title>
    </head>
    <body>
        <h1>Result</h1>
        <a>
            Your Name is
            {% if user %}
                {{ user }}
            {% else %}
                ?
            {% endif %}
        </a>
    </body>
</html>

3. 테스트

브라우저 (크롬)을 통해 페이지 접속 (127.0.0.1:9999)

index.html 라우팅 결과

LOGIN 버튼을 클릭하면 (FTP 접속할때처럼) 로그인 창이 뜬다

extension에서 브라우저로 하여금 HTTP Basic Auth 호출 시 기본적으로 호출되는 인터페이스인듯

로그인 창 호출

잘못된 로그인 정보를 기입하고 로그인 버튼을 클릭할 경우 로그인 창이 다시 뜨게 된다 (횟수 제한 없음)

서버 로그를 보면 루틴을 알 수 있다

verify_password failed >> username: user2, password: 1234
127.0.0.1 - - [08/Jan/2021 22:01:39] "GET /login HTTP/1.1" 401 -
verify_password failed >> username: user3, password: 12345
127.0.0.1 - - [08/Jan/2021 22:01:43] "GET /login HTTP/1.1" 401 -
verify_password failed >> username: user1, password: password2
127.0.0.1 - - [08/Jan/2021 22:01:48] "GET /login HTTP/1.1" 401 -

401 - Unauthorized 값을 /login 으로 다시 라우팅된다

로그인 횟수 제한을 두고자 한다면 해당 라우팅에서 구문을 추가해주면 될 것 같다

 

미리 정의해둔 users 내에 있는 사용자 정보로 로그인 성공하면 /result 로 라우팅되고 다음과 같은 결과를 볼 수 있다

/result 라우팅 결과 (로그인 성공 시)

서버 로그

verify_password success >> username: user1, password: password1
127.0.0.1 - - [08/Jan/2021 22:07:29] "GET /login HTTP/1.1" 200 -

200 - OK 결과와 함께 verify_password 데코레이터 통과 후 login 함수 내에서 result.html 템플릿 렌더링까지 이루어진다

 

HTTP Basic 인증의 특성상 인증 결과는 클라이언트 측의 브라우저 내부 쿠키로 남게 되어 다음번 로그인 호출 시 로그인 창 없이 바로 user1으로 인증이 이루어진다

(다시 한번 언급하자면, 로그아웃 등의 기능은 session을 이용해 구현해야 한다)

크롬 브라우저의 쿠키 삭제 기능

4. 정리

homebridge와 같은 IoT 허브 (브릿지)는 특수한 경우를 제외하고는 클라이언트(제어 디바이스, 센서 등)들은 1회성으로 접속 후 데이터 송/수신이 이루어 질 것이기 때문에 별도의 세션을 가지거나 로그아웃 프로세스가 필요하지 않다

이런 경우를 위해서는 충분히 활용가치가 높은 flask extension이라고 생각한다

반응형