Flask Basic Auth的實現

Gevin發表於2016-06-20

Flask Basic Auth的實現
Flask Basic Auth

本文首發於Gevin的部落格

原文連結:Flask Basic Auth的實現

未經 Gevin 授權,禁止轉載

RESTful API開發中,Authentication(認證)機制的實現通常『非常必要』。Basic Auth是配合RESTful API 使用的最簡單的認證方式,只需提供使用者名稱密碼即可,Basic Auth 常用於開發和測試階段,Flask 作為一個微框架,雖然沒有整合Basic Auth的實現,但相關資訊均已提供,我們只需做簡單封裝,即可實現Basic Auth。

1. Basic Auth的實現

思路:利用request.authorization和裝飾器

Basic Auth機制,客戶端向伺服器發請求時,會在請求的http header中提供使用者名稱和密碼作為認證資訊,格式為"Authorization":'basic '+b64Val,其中b64Val為經過base64轉碼後的使用者名稱密碼資訊,即b64Val=base64.b64encode('username:password')

Flask 中,客戶端的請求均由request類處理,對於Basic Auth中的傳來的使用者名稱和密碼,已經儲存到request.authorization中,即:

    auth = request.authorization
    username = auth.username
    password = auth.password複製程式碼

因此,Flask中只要封裝一個basic auth裝飾器,然後把該裝飾器應用到需要使用basic auth的API中即可:

def basic_auth_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            return not_authenticated()
        return f(*args, **kwargs)
    return decorated複製程式碼

其中,check_auth()not_authenticated()函式實現如下:

def check_auth(username, password):
    """This function is called to check if a username /
    password combination is valid.
    """
    try:
        user = models.User.objects.get(username=username)
    except models.User.DoesNotExist:
        user = None

    if user and user.verify_password(password):
        return True

    return False

def not_authenticated():
    """Sends a 401 response that enables basic auth"""
    return Response(
    'Could not verify your access level for that URL.\n'
    'You have to login with proper credentials', 401,
    {'WWW-Authenticate': 'Basic realm="Login Required"'})複製程式碼

API中使用該裝飾器時,即可完成API的basic auth認證機制。對function view和class based view實現分別如下:

# function View
@basic_auth_required
def test_get_current_user():
    return jsonify(username=current_user.username)

# Class Based View
class TestAPIView(MethodView):
    decorators = [basic_auth_required, ]

    def get(self):
        data = {'a':1, 'b':2, 'c':3}
        return jsonify(data)

    def post(self):
        data = request.get_json()
        return jsonify(data)

    def put(self):
        data = request.get_json()
        return jsonify(data)

    def patch(self):
        data = request.get_json()
        return jsonify(data)複製程式碼

2. Basic Auth 與 Flask-login的結合

Flask-login中的current_user是一個非常好用的物件,使業務邏輯與當前使用者產生交集時,使用者相關資訊能夠信手即來。在RESTful API開發中,很多API的業務邏輯也會與認證使用者發生交集,如果current_user依然有效,很多相關業務邏輯可以解耦,程式碼也會更加優雅。但Flask-login中,current_user預設是基於session的,API中不存在sessioncurrent_user無法使用。所幸強大的Flask-login 內建了request_loader callback,允許通過header中的資訊載入當前使用者,方法如下:

@login_manager.request_loader
def load_user_from_request(request):
    auth = request.authorization
    if not auth:
        return None
    try:
        user = User.objects.get(username=auth.username)
    except User.DoesNotExist:
        user = None
    return user複製程式碼

把上面程式碼放到專案中,就可以在API的業務邏輯中把basic authcurrent user結合用起來,如:

@basic_auth_required
def test_get_current_user():
    return jsonify(username=current_user.username)複製程式碼

相關文章