Flask uses itsdangerous to generate tokens
itsdangerous example
(venv)$python manage.py shell >>> from manage import app >>> from itsdangerous import TimedJSONWebSignatureSerializer as Serializer >>> s = Serializer(app.config['SECRET_KEY'], expires_in = 3600) >>> token = s.dumps({ 'confirm': 23 }) >>> token 'eyJhbGciOiJIUzI1NiIsImV4cCI6MTM4MTcxODU1OCwiaWF0IjoxMzgxNzE0OTU4fQ.ey ...' >>> data = s.loads(token) >>> data {u'confirm':23}
-
itsdangerous provides a variety of methods to generate tokens, among which the TimedJSONWebSignatureSerializer class generates JSON Web Signatures (JWS) with expiration time. The parameter received by the constructor of this class is a key, which can be used in the flash program_ Key settings.
-
dumps() method generates an encrypted signature for the specified data, and then serializes the data and signature to generate a token string. expires_ The in parameter sets the expiration time of the token, in seconds.
-
To decode the token, the serialized object provides the loads() method, whose only parameter is the token string. This method
The signature and expiration time will be verified. If passed, the original data will be returned. If the token provided to the loads() method is incorrect
If it is true or expired, an exception is thrown.
Actual project use process
This example scenario is the user mailbox authentication function.
-
Add to user model class (models.py)
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer from flask import current_app from . import db class User(UserMixin, db.Model): # ... confirmed = db.Column(db.Boolean, default=False) # Judge whether the user is authenticated. The default is False # Generate token token def generate_confirmation_token(self, expiration=3600): # Expiration time s = Serializer(current_app.config['SECRET_KEY'], expiration) # Serialization key return s.dumps({'confirm': self.id}) # Validate token token def confirm(self, token): s = Serializer(current_app.config['SECRET_KEY']) # Serialization key try: data = s.loads(token) # The method throws an exception when validation fails or the token times out except: return False if data.get('confirm') != self.id: # Verify the user id to prevent the token generation method from being disclosed return False self.confirmed = True # Verification passed db.session.add(self) # Save this user return True
-
Send token authentication email (views.py)
from ..email import send_email # Mailbox verification method @auth.route('/register', methods = ['GET', 'POST']) def register(): form = RegistrationForm() if form.validate_on_submit(): # ... db.session.add(user) db.session.commit() token = user.generate_confirmation_token() # Get authentication token send_email(user.email, 'Confirm Your Account', 'auth/email/confirm', user=user, token=token) # Send mailbox and configure template parameters: user and token flash('A confirmation email has been sent to you by email.') return redirect(url_for('main.index')) return render_template('auth/register.html', form=form) # To prevent mail loss, send the authentication mail again. At this time, the user is current_ User (logged in user, i.e. target user). from flask_login import current_user @auth.route('/confirm') @login_required def resend_confirmation(): token = current_user.generate_confirmation_token() send_email(current_user.email, 'Confirm Your Account', 'auth/email/confirm', user=current_user, token=token) flash('A new confirmation email has been sent to you by email.') return redirect(url_for('main.index'))
-
After user authentication, verify the token (views.py)
from flask.ext.login import current_user @auth.route('/confirm/<token>') @login_required # The fallk login extension method must be logged in before it can be called. def confirm(token): if current_user.confirmed: # Judge whether the user has been authenticated return redirect(url_for('main.index')) if current_user.confirm(token): # The user requests the route and calls the verification function for verification. flash('You have confirmed your account. Thanks!') # success else: flash('The confirmation link is invalid or has expired.') # fail return redirect(url_for('main.index'))
- Login provided by flask login_ The required modifier will protect the route. Therefore, after the user clicks the link in the confirmation email, he must log in first, and then execute the view function.
- This function first checks whether the logged in user has been confirmed. If so, it will redirect to the home page, because obviously there is no need to do anything at this time. This process can avoid the extra work caused by the user accidentally clicking the confirmation token many times.
- Since the token confirmation is completely completed in the User model, the view function only needs to call the confirm() method, and then display different Flash messages according to the confirmation results. After the confirmation is successful, the value of the confirmed attribute in the User model will be modified and added to the session. After the request is processed, the two operations will be submitted to the database.
-
When unconfirmed users are allowed to log in, but only one page is displayed. This page requires the user to confirm the account before obtaining permission, you can use before provided by Flask_ Request hook completion: (before_request hook can only be applied to requests belonging to this route. If you want to use a hook for program global requests, you must use the before_app_request decorator):
@auth.before_app_request def before_request(): if current_user.is_authenticated() \ and not current_user.confirmed \ and request.endpoint[:5] != 'auth.' \ and request.endpoint != 'static': return redirect(url_for('auth.unconfirmed')) @auth.route('/unconfirmed') def unconfirmed(): if current_user.is_anonymous() or current_user.confirmed: return redirect(url_for('main.index')) return render_template('auth/unconfirmed.html')
Before when the following three conditions are met at the same time_ app_ The request handler will intercept the request and be redirected to the / auth/unconfirmed route, displaying a page to confirm the account related information, such as prompting to bind the mobile phone.
- The user is logged in (current_user.is_authenticated() must return True).
- The user's account has not been confirmed.
- The requested endpoint is not in the authentication route. (use request.endpoint to obtain permissions for accessing authentication routes, because the purpose of these routes is to allow users to confirm accounts or perform other account management operations.)