Authentication management
New database
create database `flasky` default character set utf8 collate utf8_general_ci;
Required plug-ins
Flash mail: used to send authentication information Flask login: used to manage session s Werzeug + its dangerous: used for adding salt to the password hash Flash WTF: form object creation Flash bootstrap: bootstrap rendering
Establish certification blueprint
# auth/__init__.py # coding: utf-8 from flask import Blueprint auth_bp = Blueprint("/",__name__) from . import views,routes,models # app/__init__.py def create_app(config_name): ... from app.auth import auth_bp app.register_blueprint(auth_bp)
Building user ORM model
# auth/models.py # coding: utf-8 from werkzeug.security import generate_password_hash,check_password_hash from app import db class User(db.Model): __tablename__ = "users" id = db.Column(db.Integer,primary_key=True) username = db.Column(db.String(12)) email = db.Column(db.String(50)) password_hash = db.Column(db.String(128)) role_id = db.Column(db.Integer,db.ForeignKey('roles.id')) @property def password(self): raise AttributeError("password is not readable attribute") @password.setter def password(self,password): self.password_hash = generate_password_hash(password) def verify_password(self,password): return check_password_hash(self.password_hash,password)
Permission assignment
Permission level setting
name | permission name | permission value |
---|---|---|
Follow users | FOLLOW | 1 |
Comment on posts made by others | COMMENT | 2 |
Write articles | WRITE | 4 |
Moderate comments | MODERATE | 8 |
Admin | ADMIN | 16 |
Create a Permission object directly to represent the Permission
# auth/models.py class Permission: FOLLOW = 1 COMMENT = 2 WRITE = 4 MODERATE = 8 ADMIN = 16
Set up three user roles
role | Jurisdiction | describe |
---|---|---|
None | None | read-only |
User | FOLLOW/COMMENT/WRITE | Basic authority |
Moderator | MODERATOR | Only one permission to modify comments |
ADMIN | FOLLOW/COMMENT/WRITE/MODERATOR/ADMIN | One more administrator permission |
Build role model
# auth/models.py # coding: utf-8 from werkzeug.security import generate_password_hash,check_password_hash from app import db class Role(db.Model): __tablename__ == "roles" id = db.Column(db.Integer,primary_key=True) name = db.Column(db.String(10)) default = db.Column(db.Boolean,default=False,index=True) # It is set to only one user, and other users are False. Because the app will search for this value, it is set to index for easy search permissions = db.Column(db.Integer) users = db.relationship('User',backref='role',lazy='dynamic') def __init__(self,**kw): super(Role,self).__init__(**kw) if self.permissions is None: self.permissions = 0 # Judge whether there is permission def has_permission(self,perm): return self.permissions & perm == perm # add permission def add_permission(self,perm): if not self.has_permission(perm): self.permissions += perm # Remove authority def remove_permission(self,perm): if self.has_permission(perm): self.permissions -= perm # Reset permission def reset_permission(self): self.permissions = 0 # Insert roles @staticmethod def insert_roles(): roles = { "User":[ Permission.FOLLOW, Permission.COMMENT, Permission.WRITE, ], "Moderator":[ Permission.MODERATE ], "ADMIN":[ Permission.FOLLOW, Permission.COMMENT, Permission.WRITE, Permission.MODERATE, Permission.ADMIN, ] } # The default role is user default_role = 'User' for r in roles: # Search for three roles in the existence of database tables role = Role.query.filter_by(name=r).first() # If there is no such role, add it immediately to facilitate future expansion if role is None: role = Role(name=r) # Reset permission role.reset_permission() for perm in roles[r]: # Authority is added one by one role.add_permission(perm) # Write default user to database role.default = (role.name == default_role) db.session.add(role) db.session.commit()
Then assign roles to users
# app/auth/models.py class User(db.Model): def __init__(self,**kw): super(User,self).__init__(**kw) # Assign administrator role if self.role is None and self.email == current_app.config['FLASKY_ADMIN']: self.role = Role.query.filter_by(name="ADMIN").first() # If a user's role does not exist if self.role is None: # Assign a default role self.role = Role.query.filter_by(default=True).first() # Judge user role def can(self,perm): return self.role is not None and self.role.has_permission(perm) def is_administrator(self): return self.can(Permission.ADMIN)
Next, write the operation of inserting the role into the deploy command, and then use flash deploy to deploy
# run.py @app.cli.command() def deploy(): Role.insert_roles()
flask-migrate
pipenv install flask-migrate
Run create database table
flask db init flask db migrate -m "add users and roles table" flask db upgrade
unit testing
Configure the flash command first
# run.py @app.cli.command() def test(): import unittest tests = unittest.TestLoader().discover('tests') unittest.TextTestRunner(verbosity=2).run(tests)
Create a new tests folder under the root folder
mkdir tests
New unit test file
# tests/test_user_models.py # User model test script import unittest from app.auth.models import User class UserModelTestCase(unittest.TestCase): def test_password_enter(self): u = User(password="cat") self.assertTrue(u.password_hash is not None) def test_no_password_getter(self): u = User(password='cat') with self.assertRaises(AttributeError): u.password def test_password_verification(self): u = User(password='cat') self.assertTrue(u.verify_password('cat')) def test_password_salts_are_random(self): u = User(password="cat") u1 = User(password = "cat") self.assertFalse(u1.password_hash == u.password_hash) # Role model test script # tests/test_user_role.py import unittest from app.auth.models import Role,Permission,User class TestUserRole(unittest.TestCase): def test_default_user(self): u = User(username="john",password="cat") self.assertTrue(u.can(Permission.COMMENT)) self.assertTrue(u.can(Permission.WRITE)) self.assertTrue(u.can(Permission.FOLLOW)) def test_moderator_user(self): u = User(username="john_moder",password="123") u.role = Role.query.filter_by(name="Moderator").first() self.assertTrue(u.can(Permission.MODERATE)) self.assertFalse(u.can(Permission.FOLLOW)) self.assertFalse(u.can(Permission.COMMENT)) self.assertFalse(u.can(Permission.WRITE)) def test_admin_user(self): u = User(username="john_admin",password="123") u.role = Role.query.filter_by(name="ADMIN").first() self.assertTrue(u.can(Permission.MODERATE)) self.assertTrue(u.can(Permission.FOLLOW)) self.assertTrue(u.can(Permission.COMMENT)) self.assertTrue(u.can(Permission.WRITE)) self.assertTrue(u.can(Permission.ADMIN))
Run deployment initialization and unit tests
flask deploy flask test
Code: https://github.com/TheFifthMa...
Reference resources
Recommended reading: oreally. Flask. Web. Development. 2nd. Edition