本文首發(fā)于Gevin的博客
未經(jīng) Gevin 授權(quán)侠碧,禁止轉(zhuǎn)載
RESTful API開發(fā)中,Authentication(認證)機制的實現(xiàn)通常『非常必要』。Basic Auth是配合RESTful API 使用的最簡單的認證方式,只需提供用戶名密碼即可仿滔,Basic Auth 常用于開發(fā)和測試階段桨踪,F(xiàn)lask 作為一個微框架伪节,雖然沒有集成Basic Auth的實現(xiàn)吼砂,但相關(guān)信息均已提供,我們只需做簡單封裝美旧,即可實現(xiàn)Basic Auth渤滞。
<a name="basic_auth"></a> 1. Basic Auth的實現(xiàn)
思路:利用request.authorization和裝飾器
Basic Auth機制,客戶端向服務(wù)器發(fā)請求時榴嗅,會在請求的http header中提供用戶名和密碼作為認證信息妄呕,格式為"Authorization":'basic '+b64Val
,其中b64Val為經(jīng)過base64轉(zhuǎn)碼后的用戶名密碼信息嗽测,即b64Val=base64.b64encode('username:password')
Flask 中绪励,客戶端的請求均由request
類處理,對于Basic Auth中的傳來的用戶名和密碼论咏,已經(jīng)保存到request.authorization
中优炬,即:
auth = request.authorization
username = auth.username
password = auth.password
因此,F(xiàn)lask中只要封裝一個basic auth裝飾器厅贪,然后把該裝飾器應(yīng)用到需要使用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()
函數(shù)實現(xiàn)如下:
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實現(xiàn)分別如下:
# 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)
<a name="flask-login"></a> 2. Basic Auth 與 Flask-login的結(jié)合
Flask-login中的current_user
是一個非常好用的對象,使業(yè)務(wù)邏輯與當(dāng)前用戶產(chǎn)生交集時眉抬,用戶相關(guān)信息能夠信手即來贯吓。在RESTful API開發(fā)中,很多API的業(yè)務(wù)邏輯也會與認證用戶發(fā)生交集蜀变,如果current_user
依然有效悄谐,很多相關(guān)業(yè)務(wù)邏輯可以解耦,代碼也會更加優(yōu)雅库北。但Flask-login中爬舰,current_user
默認是基于session
的们陆,API中不存在session
,current_user
無法使用情屹。所幸強大的Flask-login 內(nèi)置了request_loader
callback坪仇,允許通過header中的信息加載當(dāng)前用戶,方法如下:
@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的業(yè)務(wù)邏輯中把basic auth
和 current user
結(jié)合用起來椅文,如:
@basic_auth_required
def test_get_current_user():
return jsonify(username=current_user.username)