Using flask to design restful API interfaces with certified tokens [translate]

Source: Internet
Author: User
Tags oauth

In a previous article, using Python's flask to implement a RESTful API server side simply demonstrated the flask Real API server, which mentioned that because of the stateless principle, there is no session cookie, if the access needs to verify the interface, A client request must send a user name and password each time. Typically in real-world apps, the username and password are not sent every time.

In this article, we talk about the method of generating tokens.

Complete example of the code

Can be found on the Github:rest-auth. The author welcomed everyone up to discuss with him.

Create a user database

This example is closer to the real project and will use the Flask-sqlalchemy (ORM) module to manage the user database.

The user model is very simple. Each user has only username and Password_hash two properties.

Class User (db. Model):    __tablename__ = ' users '    id = db. Column (db. Integer, Primary_key = True)    username = db. Column (db. String (+), index = True)    Password_hash = db. Column (db. String (128))

For security reasons, plaintext passwords cannot be stored directly, and must be hashed before they can be deposited into the database. If the database is taken off, it is more difficult to crack.

The password should never be clear in the database.

Password Hashing

This uses the Passlib library to hash the password.

Passlib provides several hash algorithms. The Custom_app_context module is based on the Sha256_crypt encryption algorithm, which is very simple to use.

There are two ways to add password hashing and validation to the user model:

From Passlib.apps import Custom_app_context as Pwd_contextclass User (db. Model):    # ...    def hash_password (self, password):        self.password_hash = pwd_context.encrypt (password)    def verify_password ( Self, password):        return pwd_context.verify (password, self.password_hash)

When a new user registers, or changes the password, the Hash_password () function is called and the original password is passed in as a parameter to the Hash_password () function.

The Verify_password () function is called when the user's password is validated and returns True if the password is correct, or false if it is incorrect.

The hash algorithm is unidirectional, meaning it can only hash the password, but cannot restore the password. But these algorithms are absolutely reliable, input the same content, then the content of the hash will be the same. Usually when registering or validating, the comparison is the result of the hash.

User Registration

In this example, the client sends a POST request to the/api/users, and the body part of the request must be in JSON format and contains the username and password fields.

Code implemented by Flask:

@app. Route ('/api/users ', methods = [' POST ']) def new_user ():    username = request.json.get (' username ')    password = Request.json.get (' password ')    if username is None or password is none:        abort (+) # missing arguments    if User . query.filter_by (username = username). First () is not None:        abort (+) # existing user    user = User (username = Usern AME)    user.hash_password (password)    db.session.add (user)    db.session.commit ()    return jsonify ({' Username ': User.username}), 201, {' Location ': url_for (' get_user ', id = user.id, _external = True)}

This is a very simple function. Just use the requested JSON inside to get username and password two parameters.

If the parameter validation passes, a user instance is created and the password hash is stored in the database.

The request response returns a JSON-formatted object with a status code of 201, and in the HTTP header defines the URI of the location that points to the user you just created.

Note: The Get_user function is not implemented here and is specifically checked to see GitHub.

Try sending a registration request using curl:

$ curl-i-X post-h "Content-type:application/json"-d ' {"username": "OK", "password": "Python"} ' http://127.0.0.1:5000/ api/usershttp/1.0 201 createdcontent-type:application/jsoncontent-length:27location:http://127.0.0.1:5000/api/ users/1server:werkzeug/0.9.4 Python/2.7.3date:thu, 19:56:39 gmt{  "username": "OK"}

Usually in a formal server, it is best to use HTTPS communication. This way of logging in, clear communication is very easy to intercept.

Simple password-based authentication

Now let's assume that there is an API that is only open to already registered users. The access point is/api/resource.

This is done using the HTTP BASIC authentication method, and I plan to use the Flask-httpauth extension to implement this feature.

After importing the Flask-httpauth extension module, add the login_required adorner for the corresponding function:

From Flask.ext.httpauth Import Httpbasicauthauth = Httpbasicauth () @app. Route ('/api/resource ') @auth. Login_ Requireddef Get_resource ():    return jsonify ({' Data ': ' Hello,%s! '% g.user.username})

Then Flask-httpauth (login_required decorator) needs to know how to validate user information, which requires a specific way to implement security validation.

One way is to be very flexible, by implementing the Verify_password callback function to verify the user name and password, verify that by returning true, otherwise false is returned. Flask-httpauth then calls the callback function, which makes it easy to customize the validation method. (Note: Functional programming for Python decorators)

The specific implementation code is as follows:

@auth. Verify_passworddef Verify_password (username, password):    user = User.query.filter_by (username = username). First ()    if not user or not User.verify_password (password):        return False    g.user = user    return True

If the username and password are validated, the user pair will be stored in the G-pair image of flask. (Note: Object g is stored in the app context and is no longer in the request context, meaning that it is accessible even in the context of the application, not only in the context of the request.) ) to facilitate the use of other functions.

Let's use a registered user to request a look:

$ curl-u ok:python-i-x GET http://127.0.0.1:5000/api/resourceHTTP/1.0 okcontent-type:application/ jsoncontent-length:30server:werkzeug/0.9.4 Python/2.7.3date:thu, 20:02:25 gmt{  "data": "Hello, ok!"}

If a login error occurs, the following is returned:

$ curl-u miguel:ruby-i-x GET http://127.0.0.1:5000/api/resourceHTTP/1.0 401 unauthorizedcontent-type:text/html; Charset=utf-8content-length:19www-authenticate:basic realm= "Authentication Required" server:werkzeug/0.9.4 Python/ 2.7.3date:thu, 20:03:18 gmtunauthorized Access

Again, the real API server is best to communicate under HTTPS.

Token-based authentication

Because the need to send a user name and password every request, the client needs to store the authentication information to send, so it is very inconvenient, even if the transmission under HTTPS, there is a risk.

Better than the previous password authentication method is to use token authentication requests.

The principle is that the first time the client and the server Exchange authentication information to obtain an authentication token, the subsequent request to use this token for the request.

Token usually gives an expiration time, and when it exceeds this time, it becomes invalid and requires a new token to be generated. So even if token leaks, the harm is only within the effective time.

Many ways to achieve token. A simple approach is to produce a fixed-length random sequence character stored with the user name and password in the database, possibly with an expiration time. This token becomes a string of ordinary characters that can be easily compared with other strings, and can check if the time expires.

A more complex implementation would be to use the digital signature information as tokens instead of storing tokens on the server side. The benefit of this is that tokens generated by the user's digital signature can be tamper-proof.

Flask uses a similar approach to digital signatures to implement encrypted cookies, where we use the Itsdangerous library to implement them.

The method for generating tokens and validating tokens can be attached to the user model:

From itsdangerous import Timedjsonwebsignatureserializer as Serializerclass User (db. Model):    # ...    def generate_auth_token (self, expiration =):        s = Serializer (app.config[' Secret_key '), expires_in = expiration) C3/>return s.dumps ({' id ': self.id})    @staticmethod    def verify_auth_token (token):        s = Serializer ( app.config[' Secret_key ')        try:            data = s.loads (token)        except signatureexpired:            return None # Valid token, but expired        except badsignature:            return None # Invalid token        user = User.query.get (data[' id ')) C14/>return User

In the Generate_auth_token () function, token is actually an encrypted dictionary that contains the user's ID and the default is 10 minutes (600 seconds) of the expiration time.

The implementation of Verify_auth_token () is a static method, because token is simply a decoding of the user ID inside the retrieval. After obtaining the user ID, you can get the user profile in the database.

Try using a new access point to have the client request a token:

@app. Route ('/api/token ') @auth. Login_requireddef Get_auth_token ():    token = G.user.generate_auth_token ()    Return jsonify ({' token ': Token.decode (' ASCII ')})

Note that this access point is protected by the Flask-httpauth extended auth.login_required adorner, and the request requires a username and password.

The above returns a token string, and the following request will contain the token.

HTTP Basic authentication protocol does not specifically require the use of user name and password for authentication, HTTP header can use two fields to transfer authentication information, for token authentication, only need to send token as a user name, password field can be slightly.

In summary, some certifications still use username and password authentication, and the other part directly uses the token authentication obtained. The Verify_password callback function needs to include two ways to verify:

@auth. Verify_passworddef verify_password (username_or_token, password):    # First try to authenticate by token    user = User.verify_auth_token (username_or_token)    if not User:        # try to authenticate with Username/password        user = User.query.filter_by (username = username_or_token). First ()        if not User or not User.verify_password (password) :            return False    g.user = user    return True

Modify the original Verify_password callback function to add two kinds of validations. Start using User Name field as token, if not token, use username and password authentication.

Use the Curl test request to obtain an authentication token:

$ curl-u ok:python-i-x GET http://127.0.0.1:5000/api/tokenHTTP/1.0 Okcontent-type:application/jsoncontent-length : 139server:werkzeug/0.9.4 Python/2.7.3date:thu, 20:04:15 gmt{  "token": " EyJhbGciOiJIUzI1NiIsImV4cCI6MTM4NTY2OTY1NSwiaWF0IjoxMzg1NjY5MDU1fQ.eyJpZCI6MX0.XbOEFJkhjHJ5uRINh2JA1BPzXjSohKYDRT472wGOvj C "}

Try to access the protected API using token one:

$ curl-u EyJhbGciOiJIUzI1NiIsImV4cCI6MTM4NTY2OTY1NSwiaWF0IjoxMzg1NjY5MDU1fQ.eyJpZCI6MX0.XbOEFJkhjHJ5uRINh2JA1BPzXjSohKYDRT472wGOvj C:unused-i-X GET http://127.0.0.1:5000/api/resourceHTTP/1.0 okcontent-type:application/jsoncontent-length: 30server:werkzeug/0.9.4 Python/2.7.3date:thu, 20:05:08 gmt{  "data": "Hello, ok!"}

Note that the request contains the unused field. Placeholder for the replacement password just to identify it.

OAuth Authentication

When it comes to restful authentication, the OAuth protocol is often mentioned.

Why is OAuth?

is typically a validation method that allows an application to access data or services from another application.

For example, if a website or app asks you for permission to access your Facebook account, and submits something to your timeline. In this case, you are the resource owner (you own your Facebook timeline), the third-party app is the consumer, and Facebook is the provider. If you authorize access to allow consumers to write things up on your timeline, there is no need to provide your Facebook login information.

OAuth is not appropriate for Client/server's RESTful API, and is typically used in your RESTful API to allow third-party applications (consumers) to access.

The above example is that direct communication between client/server does not need to hide authentication information, the client sends authentication request information directly to the server side.

Originally from: Http://blog.miguelgrinberg.com/post/restful-authentication-with-flask

Using flask to design restful API interfaces with certified tokens [translate]

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.