本文首发于我的 微信公众账号 ,欢迎大家扫码关注(二维码见文章底部)
RESTful API开发中,Authentication(认证)机制的实现通常『非常必要』。Basic Auth是配合RESTful API 使用的最简单的认证方式,只需提供用户名密码即可,Basic Auth 常用于开发和测试阶段,Flask 作为一个微框架,虽然没有集成Basic Auth的实现,但相关信息均已提供,我们只需做简单封装,即可实现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)
Flask-login中的 current_user
是一个非常好用的对象,使业务逻辑与当前用户产生交集时,用户相关信息能够信手即来。在RESTful API开发中,很多API的业务逻辑也会与认证用户发生交集,如果 current_user
依然有效,很多相关业务逻辑可以解耦,代码也会更加优雅。但Flask-login中, current_user
默认是基于 session
的,API中不存在 session
, current_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 auth
和 current user
,如:
@basic_auth_required def test_get_current_user(): return jsonify(username=current_user.username)