效果图:
Flask-Login is a Flask extension that provides a framework for handling user authentication.This post will give you a basic tutorial of the Flask-Login mechanism for token based authentication.
The goal of this post is to give a very basic introduction to token based authentication using Flask-Login.Usually the user credentials are stored in a database, with passwords hashed. However the authenticationmechanism can be understood without having to worry about database, and various token generation algorithms.As a first step lets focus on just understanding the authentication mechanism. Then in a subsequent postwe will handle other important parts.
To run this example, you will need flask and flask-login with their dependencies installed.This can be done usingpip as shown below:
>pip install flask >pip install flask-login
Example Code
Here is the full source code that we are about to discuss:
# Author: Gouthaman Balaraman
# http://gouthamanbalaraman.com/minimal-flask-login-example.html
from flask import Flask, Response
from flask.ext.login import LoginManager, UserMixin, login_required
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
class User(UserMixin):
# proxy for a database of users
user_database = {"JohnDoe": ("JohnDoe","John"),
"JaneDoe": ("JaneDoe","Jane")}
def __init__(self,username,password):
self.id = username
self.password = password
@classmethod
def get(cls,id):
return cls.user_database.get(id)
@login_manager.request_loader
def load_user(request):
token = request.headers.get('Authorization')
if token is None:
token = request.args.get('token')
if token is not None:
username,password = token.split(":")# naive token
user_entry = User.get(username)
if (user_entry is not None):
user = User(user_entry[0],user_entry[1])
if (user.password == password):
return user
return None
@app.route("/",methods=["GET"])
def index():
return Response(response="Hello World!",status=200)
@app.route("/protected/",methods=["GET"])
@login_required
def protected():
return Response(response="Hello Protected World!",status=200)
if __name__ == '__main__':
app.config["SECRET_KEY"]="ITSASECRET"
app.run(port=5000,debug=True)
Code Explained
Lets delve deeper into this example, and I will explain each part of the code in greater detail here.
from flask import Flask, Response
from flask.ext.login import LoginManager, UserMixin, login_required
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
The first five lines of the code import the required modules, and initializes the Flaskapp. ThentheLoginManager instance is created and then is configure for login. Now lets try to understandtheUser class.
class User(UserMixin): # proxy for a database of users user_database = {"JohnDoe": ("JohnDoe", "John"), "JaneDoe": ("JaneDoe", "Jane")} def __init__(self, username, password): self.id = username self.password = password @classmethod def get(cls,id): return cls.user_database.get(id)
Here I have created the User class by overloading theUserMixin class. TheUserMixin classimplements some of the default methods, and hence is a convenient starting point. Thedictuser_databaseis a proxy for all the database code one would need. I am abstracting this away as adict for simplicity.Theget class method returns the user data fromuser_database.
For the LoginManager to handle authentication, we have to provide a method for it to load user.Here I use the generic@login_manager.request_loader decorator to decorate theload_user function.The expected behavior of a request_loader is to return aUser instance if the provided credentials arevalid, and returnNone otherwise.
@login_manager.request_loader def load_user(request): token = request.headers.get('Authorization') if token is None: token = request.args.get('token') if token is not None: username,password = token.split(":") # naive token user_entry = User.get(username) if (user_entry is not None): user = User(user_entry[0],user_entry[1]) if (user.password == password): return user return None
The load_user looks for a token in Authorization header, or the request arguments. If a tokenis provided, then I return an instance ofUser if the token is valid, and returnNone otherwise.Here I assume that a valid token would be of the form<username>:<password>. This is a naive token,and should not be used in practice. Using serializers fromitsdangerous package can come handy. Wewill touch upon these issues in another post.
Once this setup is done, in order to require authentication for a route, use the@login_requireddecorator.
Run the above script, and if you visit the LocalHostUnAuthenticated route withouta token you will get a 401 Unauthorized message. If you pass a token toLocalHostAuthenticated, then you will be allowed access to the protected page.
Conclusion
This article explained how to write token based authentication using Flask-Login extension. The focusof this article was explaining the basic workings offlask-login without having to setup database oreven the token generation. Once the basic plumbing is setup, one can extend this example in two ways:
- Securing Authentication Tokens
- have a database to store and retrive user credentials.